diff --git a/Logo/dottrace.svg b/Logo/dottrace.svg deleted file mode 100644 index b879517cd..000000000 --- a/Logo/dottrace.svg +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Logo/jetbrains.svg b/Logo/jetbrains.svg deleted file mode 100644 index 75d4d2177..000000000 --- a/Logo/jetbrains.svg +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Logo/resharper.svg b/Logo/resharper.svg deleted file mode 100644 index 24c987a78..000000000 --- a/Logo/resharper.svg +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Logo/rider.svg b/Logo/rider.svg deleted file mode 100644 index 82da35b0b..000000000 --- a/Logo/rider.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - rider - - - - - - - - - - - - - - diff --git a/Logo/webstorm.svg b/Logo/webstorm.svg deleted file mode 100644 index 39ab7eb97..000000000 --- a/Logo/webstorm.svg +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/README.md b/README.md index 4973be54d..e8c60546a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Prowlarr [![Build Status](https://dev.azure.com/Prowlarr/Prowlarr/_apis/build/status/Prowlarr.Prowlarr?branchName=develop)](https://dev.azure.com/Prowlarr/Prowlarr/_build/latest?definitionId=1&branchName=develop) -[![Translated](https://translate.servarr.com/widgets/servarr/-/prowlarr/svg-badge.svg)](https://translate.servarr.com/engage/prowlarr/?utm_source=widget) +[![Translation status](https://translate.servarr.com/widget/servarr/prowlarr/svg-badge.svg)](https://translate.servarr.com/engage/servarr/?utm_source=widget) [![Docker Pulls](https://img.shields.io/docker/pulls/hotio/prowlarr.svg)](https://wiki.servarr.com/prowlarr/installation/docker) ![Github Downloads](https://img.shields.io/github/downloads/Prowlarr/Prowlarr/total.svg) [![Backers on Open Collective](https://opencollective.com/Prowlarr/backers/badge.svg)](#backers) @@ -68,16 +68,16 @@ Support this project by becoming a sponsor. Your logo will show up here with a l ## JetBrains -Thank you to [JetBrains JetBrains](http://www.jetbrains.com/) for providing us with free licenses to their great tools. +Thank you to [JetBrains](http://www.jetbrains.com/) for providing us with free licenses to their great tools. -- [ReSharper ReSharper](http://www.jetbrains.com/resharper/) -- [WebStorm WebStorm](http://www.jetbrains.com/webstorm/) -- [Rider Rider](http://www.jetbrains.com/rider/) -- [dotTrace dotTrace](http://www.jetbrains.com/dottrace/) +* [ReSharper ReSharper](http://www.jetbrains.com/resharper/) +* [WebStorm WebStorm](http://www.jetbrains.com/webstorm/) +* [Rider Rider](http://www.jetbrains.com/rider/) +* [dotTrace dotTrace](http://www.jetbrains.com/dottrace/) ### License - [GNU GPL v3](http://www.gnu.org/licenses/gpl.html) -- Copyright 2010-2022 +- Copyright 2010-2024 Icon Credit - [Box vector created by freepik - www.freepik.com](https://www.freepik.com/vectors/box) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 916bac55f..dc667e803 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -9,18 +9,18 @@ variables: testsFolder: './_tests' yarnCacheFolder: $(Pipeline.Workspace)/.yarn nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages - majorVersion: '1.17.2' + majorVersion: '1.35.0' minorVersion: $[counter('minorVersion', 1)] prowlarrVersion: '$(majorVersion).$(minorVersion)' buildName: '$(Build.SourceBranchName).$(prowlarrVersion)' sentryOrg: 'servarr' sentryUrl: 'https://sentry.servarr.com' - dotnetVersion: '6.0.421' + dotnetVersion: '6.0.427' nodeVersion: '20.X' innoVersion: '6.2.2' windowsImage: 'windows-2022' - linuxImage: 'ubuntu-20.04' - macImage: 'macOS-11' + linuxImage: 'ubuntu-22.04' + macImage: 'macOS-13' trigger: branches: @@ -1169,12 +1169,12 @@ stages: submodules: true - powershell: Set-Service SCardSvr -StartupType Manual displayName: Enable Windows Test Service - - task: SonarCloudPrepare@1 + - task: SonarCloudPrepare@3 condition: eq(variables['System.PullRequest.IsFork'], 'False') inputs: SonarCloud: 'SonarCloud' organization: 'prowlarr' - scannerMode: 'MSBuild' + scannerMode: 'dotnet' projectKey: 'Prowlarr_Prowlarr' projectName: 'Prowlarr' projectVersion: '$(prowlarrVersion)' @@ -1187,21 +1187,16 @@ stages: ./build.sh --backend -f net6.0 -r win-x64 TEST_DIR=_tests/net6.0/win-x64/publish/ ./test.sh Windows Unit Coverage displayName: Coverage Unit Tests - - task: SonarCloudAnalyze@1 + - task: SonarCloudAnalyze@3 condition: eq(variables['System.PullRequest.IsFork'], 'False') displayName: Publish SonarCloud Results - - task: reportgenerator@4 + - task: reportgenerator@5.3.11 displayName: Generate Coverage Report inputs: reports: '$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml' targetdir: '$(Build.SourcesDirectory)/CoverageResults/combined' reporttypes: 'HtmlInline_AzurePipelines;Cobertura;Badges' - - task: PublishCodeCoverageResults@1 - displayName: Publish Coverage Report - inputs: - codeCoverageTool: 'cobertura' - summaryFileLocation: './CoverageResults/combined/Cobertura.xml' - reportDirectory: './CoverageResults/combined/' + publishCodeCoverageResults: true - stage: Report_Out dependsOn: diff --git a/docs.sh b/docs.sh old mode 100644 new mode 100755 index ae11bc83f..38b0e0fbc --- a/docs.sh +++ b/docs.sh @@ -1,13 +1,18 @@ +#!/bin/bash +set -e + +FRAMEWORK="net6.0" PLATFORM=$1 +ARCHITECTURE="${2:-x64}" if [ "$PLATFORM" = "Windows" ]; then - RUNTIME="win-x64" + RUNTIME="win-$ARCHITECTURE" elif [ "$PLATFORM" = "Linux" ]; then - RUNTIME="linux-x64" + RUNTIME="linux-$ARCHITECTURE" elif [ "$PLATFORM" = "Mac" ]; then - RUNTIME="osx-x64" + RUNTIME="osx-$ARCHITECTURE" else - echo "Platform must be provided as first arguement: Windows, Linux or Mac" + echo "Platform must be provided as first argument: Windows, Linux or Mac" exit 1 fi @@ -21,17 +26,23 @@ slnFile=src/Prowlarr.sln platform=Posix + if [ "$PLATFORM" = "Windows" ]; then + application=Prowlarr.Console.dll +else + application=Prowlarr.dll +fi + dotnet clean $slnFile -c Debug dotnet clean $slnFile -c Release dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids dotnet new tool-manifest -dotnet tool install --version 6.5.0 Swashbuckle.AspNetCore.Cli +dotnet tool install --version 7.3.2 Swashbuckle.AspNetCore.Cli -dotnet tool run swagger tofile --output ./src/Prowlarr.Api.V1/openapi.json "$outputFolder/net6.0/$RUNTIME/prowlarr.console.dll" v1 & +dotnet tool run swagger tofile --output ./src/Prowlarr.Api.V1/openapi.json "$outputFolder/$FRAMEWORK/$RUNTIME/$application" v1 & -sleep 30 +sleep 45 kill %1 diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 8d844cb8b..56eaaeaab 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -357,11 +357,16 @@ module.exports = { ], rules: Object.assign(typescriptEslintRecommended.rules, { - 'no-shadow': 'off', - // These should be enabled after cleaning things up - '@typescript-eslint/no-unused-vars': 'warn', + '@typescript-eslint/no-unused-vars': [ + 'error', + { + args: 'after-used', + argsIgnorePattern: '^_', + ignoreRestSiblings: true + } + ], '@typescript-eslint/explicit-function-return-type': 'off', - 'react/prop-types': 'off', + 'no-shadow': 'off', 'prettier/prettier': 'error', 'simple-import-sort/imports': [ 'error', @@ -374,7 +379,41 @@ module.exports = { ['^@?\\w', `^(${dirs})(/.*|$)`, '^\\.', '^\\..*css$'] ] } - ] + ], + + // React Hooks + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'error', + + // React + 'react/function-component-definition': 'error', + 'react/hook-use-state': 'error', + 'react/jsx-boolean-value': ['error', 'always'], + 'react/jsx-curly-brace-presence': [ + 'error', + { props: 'never', children: 'never' } + ], + 'react/jsx-fragments': 'error', + 'react/jsx-handler-names': [ + 'error', + { + eventHandlerPrefix: 'on', + eventHandlerPropPrefix: 'on' + } + ], + 'react/jsx-no-bind': ['error', { ignoreRefs: true }], + 'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }], + 'react/jsx-pascal-case': ['error', { allowAllCaps: true }], + 'react/jsx-sort-props': [ + 'error', + { + callbacksLast: true, + noSortAlphabetically: true, + reservedFirst: true + } + ], + 'react/prop-types': 'off', + 'react/self-closing-comp': 'error' }) }, { diff --git a/frontend/build/webpack.config.js b/frontend/build/webpack.config.js index 5336d6583..ceacc4f04 100644 --- a/frontend/build/webpack.config.js +++ b/frontend/build/webpack.config.js @@ -25,6 +25,7 @@ module.exports = (env) => { const config = { mode: isProduction ? 'production' : 'development', devtool: isProduction ? 'source-map' : 'eval-source-map', + target: 'web', stats: { children: false @@ -65,7 +66,7 @@ module.exports = (env) => { output: { path: distFolder, publicPath: '/', - filename: '[name]-[contenthash].js', + filename: isProduction ? '[name]-[contenthash].js' : '[name].js', sourceMapFilename: '[file].map' }, @@ -90,7 +91,7 @@ module.exports = (env) => { new MiniCssExtractPlugin({ filename: 'Content/styles.css', - chunkFilename: 'Content/[id]-[chunkhash].css' + chunkFilename: isProduction ? 'Content/[id]-[chunkhash].css' : 'Content/[id].css' }), new HtmlWebpackPlugin({ @@ -169,7 +170,7 @@ module.exports = (env) => { loose: true, debug: false, useBuiltIns: 'entry', - corejs: 3 + corejs: '3.39' } ] ] @@ -190,7 +191,7 @@ module.exports = (env) => { options: { importLoaders: 1, modules: { - localIdentName: '[name]/[local]/[hash:base64:5]' + localIdentName: isProduction ? '[name]/[local]/[hash:base64:5]' : '[name]/[local]' } } }, diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js index f657adf28..89db00f8c 100644 --- a/frontend/postcss.config.js +++ b/frontend/postcss.config.js @@ -16,6 +16,7 @@ const mixinsFiles = [ module.exports = { plugins: [ + 'autoprefixer', ['postcss-mixins', { mixinsFiles }], diff --git a/frontend/src/App/App.js b/frontend/src/App/App.tsx similarity index 68% rename from frontend/src/App/App.js rename to frontend/src/App/App.tsx index eea9b7d38..dba90a697 100644 --- a/frontend/src/App/App.js +++ b/frontend/src/App/App.tsx @@ -1,20 +1,25 @@ -import { ConnectedRouter } from 'connected-react-router'; -import PropTypes from 'prop-types'; +import { ConnectedRouter, ConnectedRouterProps } from 'connected-react-router'; import React from 'react'; import DocumentTitle from 'react-document-title'; import { Provider } from 'react-redux'; +import { Store } from 'redux'; import PageConnector from 'Components/Page/PageConnector'; import ApplyTheme from './ApplyTheme'; import AppRoutes from './AppRoutes'; -function App({ store, history }) { +interface AppProps { + store: Store; + history: ConnectedRouterProps['history']; +} + +function App({ store, history }: AppProps) { return ( - + @@ -22,9 +27,4 @@ function App({ store, history }) { ); } -App.propTypes = { - store: PropTypes.object.isRequired, - history: PropTypes.object.isRequired -}; - export default App; diff --git a/frontend/src/App/AppRoutes.js b/frontend/src/App/AppRoutes.js deleted file mode 100644 index f7a578da2..000000000 --- a/frontend/src/App/AppRoutes.js +++ /dev/null @@ -1,184 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import { Redirect, Route } from 'react-router-dom'; -import NotFound from 'Components/NotFound'; -import Switch from 'Components/Router/Switch'; -import HistoryConnector from 'History/HistoryConnector'; -import IndexerIndex from 'Indexer/Index/IndexerIndex'; -import IndexerStats from 'Indexer/Stats/IndexerStats'; -import SearchIndexConnector from 'Search/SearchIndexConnector'; -import ApplicationSettings from 'Settings/Applications/ApplicationSettings'; -import DevelopmentSettingsConnector from 'Settings/Development/DevelopmentSettingsConnector'; -import DownloadClientSettingsConnector from 'Settings/DownloadClients/DownloadClientSettingsConnector'; -import GeneralSettingsConnector from 'Settings/General/GeneralSettingsConnector'; -import IndexerSettings from 'Settings/Indexers/IndexerSettings'; -import NotificationSettings from 'Settings/Notifications/NotificationSettings'; -import Settings from 'Settings/Settings'; -import TagSettings from 'Settings/Tags/TagSettings'; -import UISettingsConnector from 'Settings/UI/UISettingsConnector'; -import BackupsConnector from 'System/Backup/BackupsConnector'; -import LogsTableConnector from 'System/Events/LogsTableConnector'; -import Logs from 'System/Logs/Logs'; -import Status from 'System/Status/Status'; -import Tasks from 'System/Tasks/Tasks'; -import UpdatesConnector from 'System/Updates/UpdatesConnector'; -import getPathWithUrlBase from 'Utilities/getPathWithUrlBase'; - -function AppRoutes(props) { - const { - app - } = props; - - return ( - - {/* - Indexers - */} - - - - { - window.Prowlarr.urlBase && - { - return ( - - ); - }} - /> - } - - - - {/* - Search - */} - - - - {/* - Activity - */} - - - - {/* - Settings - */} - - - - - - - - - - - - - - - - - - - - {/* - System - */} - - - - - - - - - - - - - - {/* - Not Found - */} - - - - ); -} - -AppRoutes.propTypes = { - app: PropTypes.func.isRequired -}; - -export default AppRoutes; diff --git a/frontend/src/App/AppRoutes.tsx b/frontend/src/App/AppRoutes.tsx new file mode 100644 index 000000000..d451a12fb --- /dev/null +++ b/frontend/src/App/AppRoutes.tsx @@ -0,0 +1,117 @@ +import React from 'react'; +import { Redirect, Route } from 'react-router-dom'; +import NotFound from 'Components/NotFound'; +import Switch from 'Components/Router/Switch'; +import HistoryConnector from 'History/HistoryConnector'; +import IndexerIndex from 'Indexer/Index/IndexerIndex'; +import IndexerStats from 'Indexer/Stats/IndexerStats'; +import SearchIndexConnector from 'Search/SearchIndexConnector'; +import ApplicationSettings from 'Settings/Applications/ApplicationSettings'; +import DevelopmentSettingsConnector from 'Settings/Development/DevelopmentSettingsConnector'; +import DownloadClientSettingsConnector from 'Settings/DownloadClients/DownloadClientSettingsConnector'; +import GeneralSettingsConnector from 'Settings/General/GeneralSettingsConnector'; +import IndexerSettings from 'Settings/Indexers/IndexerSettings'; +import NotificationSettings from 'Settings/Notifications/NotificationSettings'; +import Settings from 'Settings/Settings'; +import TagSettings from 'Settings/Tags/TagSettings'; +import UISettingsConnector from 'Settings/UI/UISettingsConnector'; +import BackupsConnector from 'System/Backup/BackupsConnector'; +import LogsTableConnector from 'System/Events/LogsTableConnector'; +import Logs from 'System/Logs/Logs'; +import Status from 'System/Status/Status'; +import Tasks from 'System/Tasks/Tasks'; +import Updates from 'System/Updates/Updates'; +import getPathWithUrlBase from 'Utilities/getPathWithUrlBase'; + +function RedirectWithUrlBase() { + return ; +} + +function AppRoutes() { + return ( + + {/* + Indexers + */} + + + + {window.Prowlarr.urlBase && ( + + )} + + + + {/* + Search + */} + + + + {/* + Activity + */} + + + + {/* + Settings + */} + + + + + + + + + + + + + + + + + + + + {/* + System + */} + + + + + + + + + + + + + + {/* + Not Found + */} + + + + ); +} + +export default AppRoutes; diff --git a/frontend/src/App/AppUpdatedModal.js b/frontend/src/App/AppUpdatedModal.js deleted file mode 100644 index abc7f8832..000000000 --- a/frontend/src/App/AppUpdatedModal.js +++ /dev/null @@ -1,30 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import Modal from 'Components/Modal/Modal'; -import AppUpdatedModalContentConnector from './AppUpdatedModalContentConnector'; - -function AppUpdatedModal(props) { - const { - isOpen, - onModalClose - } = props; - - return ( - - - - ); -} - -AppUpdatedModal.propTypes = { - isOpen: PropTypes.bool.isRequired, - onModalClose: PropTypes.func.isRequired -}; - -export default AppUpdatedModal; diff --git a/frontend/src/App/AppUpdatedModal.tsx b/frontend/src/App/AppUpdatedModal.tsx new file mode 100644 index 000000000..696d36fb2 --- /dev/null +++ b/frontend/src/App/AppUpdatedModal.tsx @@ -0,0 +1,28 @@ +import React, { useCallback } from 'react'; +import Modal from 'Components/Modal/Modal'; +import AppUpdatedModalContent from './AppUpdatedModalContent'; + +interface AppUpdatedModalProps { + isOpen: boolean; + onModalClose: (...args: unknown[]) => unknown; +} + +function AppUpdatedModal(props: AppUpdatedModalProps) { + const { isOpen, onModalClose } = props; + + const handleModalClose = useCallback(() => { + location.reload(); + }, []); + + return ( + + + + ); +} + +export default AppUpdatedModal; diff --git a/frontend/src/App/AppUpdatedModalConnector.js b/frontend/src/App/AppUpdatedModalConnector.js deleted file mode 100644 index a21afbc5a..000000000 --- a/frontend/src/App/AppUpdatedModalConnector.js +++ /dev/null @@ -1,12 +0,0 @@ -import { connect } from 'react-redux'; -import AppUpdatedModal from './AppUpdatedModal'; - -function createMapDispatchToProps(dispatch, props) { - return { - onModalClose() { - location.reload(); - } - }; -} - -export default connect(null, createMapDispatchToProps)(AppUpdatedModal); diff --git a/frontend/src/App/AppUpdatedModalContent.js b/frontend/src/App/AppUpdatedModalContent.js deleted file mode 100644 index 8cce1bc16..000000000 --- a/frontend/src/App/AppUpdatedModalContent.js +++ /dev/null @@ -1,139 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import Button from 'Components/Link/Button'; -import LoadingIndicator from 'Components/Loading/LoadingIndicator'; -import InlineMarkdown from 'Components/Markdown/InlineMarkdown'; -import ModalBody from 'Components/Modal/ModalBody'; -import ModalContent from 'Components/Modal/ModalContent'; -import ModalFooter from 'Components/Modal/ModalFooter'; -import ModalHeader from 'Components/Modal/ModalHeader'; -import { kinds } from 'Helpers/Props'; -import UpdateChanges from 'System/Updates/UpdateChanges'; -import translate from 'Utilities/String/translate'; -import styles from './AppUpdatedModalContent.css'; - -function mergeUpdates(items, version, prevVersion) { - let installedIndex = items.findIndex((u) => u.version === version); - let installedPreviouslyIndex = items.findIndex((u) => u.version === prevVersion); - - if (installedIndex === -1) { - installedIndex = 0; - } - - if (installedPreviouslyIndex === -1) { - installedPreviouslyIndex = items.length; - } else if (installedPreviouslyIndex === installedIndex && items.length) { - installedPreviouslyIndex += 1; - } - - const appliedUpdates = items.slice(installedIndex, installedPreviouslyIndex); - - if (!appliedUpdates.length) { - return null; - } - - const appliedChanges = { new: [], fixed: [] }; - appliedUpdates.forEach((u) => { - if (u.changes) { - appliedChanges.new.push(... u.changes.new); - appliedChanges.fixed.push(... u.changes.fixed); - } - }); - - const mergedUpdate = Object.assign({}, appliedUpdates[0], { changes: appliedChanges }); - - if (!appliedChanges.new.length && !appliedChanges.fixed.length) { - mergedUpdate.changes = null; - } - - return mergedUpdate; -} - -function AppUpdatedModalContent(props) { - const { - version, - prevVersion, - isPopulated, - error, - items, - onSeeChangesPress, - onModalClose - } = props; - - const update = mergeUpdates(items, version, prevVersion); - - return ( - - - {translate('AppUpdated')} - - - -
- -
- - { - isPopulated && !error && !!update && -
- { - !update.changes && -
{translate('MaintenanceRelease')}
- } - - { - !!update.changes && -
-
- {translate('WhatsNew')} -
- - - - -
- } -
- } - - { - !isPopulated && !error && - - } -
- - - - - - -
- ); -} - -AppUpdatedModalContent.propTypes = { - version: PropTypes.string.isRequired, - prevVersion: PropTypes.string, - isPopulated: PropTypes.bool.isRequired, - error: PropTypes.object, - items: PropTypes.arrayOf(PropTypes.object).isRequired, - onSeeChangesPress: PropTypes.func.isRequired, - onModalClose: PropTypes.func.isRequired -}; - -export default AppUpdatedModalContent; diff --git a/frontend/src/App/AppUpdatedModalContent.tsx b/frontend/src/App/AppUpdatedModalContent.tsx new file mode 100644 index 000000000..0bd5df6d3 --- /dev/null +++ b/frontend/src/App/AppUpdatedModalContent.tsx @@ -0,0 +1,145 @@ +import React, { useCallback, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import Button from 'Components/Link/Button'; +import LoadingIndicator from 'Components/Loading/LoadingIndicator'; +import InlineMarkdown from 'Components/Markdown/InlineMarkdown'; +import ModalBody from 'Components/Modal/ModalBody'; +import ModalContent from 'Components/Modal/ModalContent'; +import ModalFooter from 'Components/Modal/ModalFooter'; +import ModalHeader from 'Components/Modal/ModalHeader'; +import usePrevious from 'Helpers/Hooks/usePrevious'; +import { kinds } from 'Helpers/Props'; +import { fetchUpdates } from 'Store/Actions/systemActions'; +import UpdateChanges from 'System/Updates/UpdateChanges'; +import Update from 'typings/Update'; +import translate from 'Utilities/String/translate'; +import AppState from './State/AppState'; +import styles from './AppUpdatedModalContent.css'; + +function mergeUpdates(items: Update[], version: string, prevVersion?: string) { + let installedIndex = items.findIndex((u) => u.version === version); + let installedPreviouslyIndex = items.findIndex( + (u) => u.version === prevVersion + ); + + if (installedIndex === -1) { + installedIndex = 0; + } + + if (installedPreviouslyIndex === -1) { + installedPreviouslyIndex = items.length; + } else if (installedPreviouslyIndex === installedIndex && items.length) { + installedPreviouslyIndex += 1; + } + + const appliedUpdates = items.slice(installedIndex, installedPreviouslyIndex); + + if (!appliedUpdates.length) { + return null; + } + + const appliedChanges: Update['changes'] = { new: [], fixed: [] }; + + appliedUpdates.forEach((u: Update) => { + if (u.changes) { + appliedChanges.new.push(...u.changes.new); + appliedChanges.fixed.push(...u.changes.fixed); + } + }); + + const mergedUpdate: Update = Object.assign({}, appliedUpdates[0], { + changes: appliedChanges, + }); + + if (!appliedChanges.new.length && !appliedChanges.fixed.length) { + mergedUpdate.changes = null; + } + + return mergedUpdate; +} + +interface AppUpdatedModalContentProps { + onModalClose: () => void; +} + +function AppUpdatedModalContent(props: AppUpdatedModalContentProps) { + const dispatch = useDispatch(); + const { version, prevVersion } = useSelector((state: AppState) => state.app); + const { isPopulated, error, items } = useSelector( + (state: AppState) => state.system.updates + ); + const previousVersion = usePrevious(version); + + const { onModalClose } = props; + + const update = mergeUpdates(items, version, prevVersion); + + const handleSeeChangesPress = useCallback(() => { + window.location.href = `${window.Prowlarr.urlBase}/system/updates`; + }, []); + + useEffect(() => { + dispatch(fetchUpdates()); + }, [dispatch]); + + useEffect(() => { + if (version !== previousVersion) { + dispatch(fetchUpdates()); + } + }, [version, previousVersion, dispatch]); + + return ( + + {translate('AppUpdated')} + + +
+ +
+ + {isPopulated && !error && !!update ? ( +
+ {update.changes ? ( +
+ {translate('MaintenanceRelease')} +
+ ) : null} + + {update.changes ? ( +
+
{translate('WhatsNew')}
+ + + + +
+ ) : null} +
+ ) : null} + + {!isPopulated && !error ? : null} +
+ + + + + + +
+ ); +} + +export default AppUpdatedModalContent; diff --git a/frontend/src/App/AppUpdatedModalContentConnector.js b/frontend/src/App/AppUpdatedModalContentConnector.js deleted file mode 100644 index 97dd0aeb9..000000000 --- a/frontend/src/App/AppUpdatedModalContentConnector.js +++ /dev/null @@ -1,78 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { fetchUpdates } from 'Store/Actions/systemActions'; -import AppUpdatedModalContent from './AppUpdatedModalContent'; - -function createMapStateToProps() { - return createSelector( - (state) => state.app.version, - (state) => state.app.prevVersion, - (state) => state.system.updates, - (version, prevVersion, updates) => { - const { - isPopulated, - error, - items - } = updates; - - return { - version, - prevVersion, - isPopulated, - error, - items - }; - } - ); -} - -function createMapDispatchToProps(dispatch, props) { - return { - dispatchFetchUpdates() { - dispatch(fetchUpdates()); - }, - - onSeeChangesPress() { - window.location = `${window.Prowlarr.urlBase}/system/updates`; - } - }; -} - -class AppUpdatedModalContentConnector extends Component { - - // - // Lifecycle - - componentDidMount() { - this.props.dispatchFetchUpdates(); - } - - componentDidUpdate(prevProps) { - if (prevProps.version !== this.props.version) { - this.props.dispatchFetchUpdates(); - } - } - - // - // Render - - render() { - const { - dispatchFetchUpdates, - ...otherProps - } = this.props; - - return ( - - ); - } -} - -AppUpdatedModalContentConnector.propTypes = { - version: PropTypes.string.isRequired, - dispatchFetchUpdates: PropTypes.func.isRequired -}; - -export default connect(createMapStateToProps, createMapDispatchToProps)(AppUpdatedModalContentConnector); diff --git a/frontend/src/App/ApplyTheme.tsx b/frontend/src/App/ApplyTheme.tsx index 392c49049..ec9cd037f 100644 --- a/frontend/src/App/ApplyTheme.tsx +++ b/frontend/src/App/ApplyTheme.tsx @@ -1,13 +1,9 @@ -import React, { Fragment, ReactNode, useCallback, useEffect } from 'react'; +import { useCallback, useEffect } from 'react'; import { useSelector } from 'react-redux'; import { createSelector } from 'reselect'; import themes from 'Styles/Themes'; import AppState from './State/AppState'; -interface ApplyThemeProps { - children: ReactNode; -} - function createThemeSelector() { return createSelector( (state: AppState) => state.settings.ui.item.theme || window.Prowlarr.theme, @@ -17,7 +13,7 @@ function createThemeSelector() { ); } -function ApplyTheme({ children }: ApplyThemeProps) { +function ApplyTheme() { const theme = useSelector(createThemeSelector()); const updateCSSVariables = useCallback(() => { @@ -31,7 +27,7 @@ function ApplyTheme({ children }: ApplyThemeProps) { updateCSSVariables(); }, [updateCSSVariables, theme]); - return {children}; + return null; } export default ApplyTheme; diff --git a/frontend/src/App/ColorImpairedContext.js b/frontend/src/App/ColorImpairedContext.ts similarity index 100% rename from frontend/src/App/ColorImpairedContext.js rename to frontend/src/App/ColorImpairedContext.ts diff --git a/frontend/src/App/ConnectionLostModal.js b/frontend/src/App/ConnectionLostModal.tsx similarity index 54% rename from frontend/src/App/ConnectionLostModal.js rename to frontend/src/App/ConnectionLostModal.tsx index 5c08f491f..f08f2c0e2 100644 --- a/frontend/src/App/ConnectionLostModal.js +++ b/frontend/src/App/ConnectionLostModal.tsx @@ -1,5 +1,4 @@ -import PropTypes from 'prop-types'; -import React from 'react'; +import React, { useCallback } from 'react'; import Button from 'Components/Link/Button'; import Modal from 'Components/Modal/Modal'; import ModalBody from 'Components/Modal/ModalBody'; @@ -10,36 +9,31 @@ import { kinds } from 'Helpers/Props'; import translate from 'Utilities/String/translate'; import styles from './ConnectionLostModal.css'; -function ConnectionLostModal(props) { - const { - isOpen, - onModalClose - } = props; +interface ConnectionLostModalProps { + isOpen: boolean; +} + +function ConnectionLostModal(props: ConnectionLostModalProps) { + const { isOpen } = props; + + const handleModalClose = useCallback(() => { + location.reload(); + }, []); return ( - - - - {translate('ConnectionLost')} - + + + {translate('ConnectionLost')} -
- {translate('ConnectionLostToBackend')} -
+
{translate('ConnectionLostToBackend')}
{translate('ConnectionLostReconnect')}
- @@ -48,9 +42,4 @@ function ConnectionLostModal(props) { ); } -ConnectionLostModal.propTypes = { - isOpen: PropTypes.bool.isRequired, - onModalClose: PropTypes.func.isRequired -}; - export default ConnectionLostModal; diff --git a/frontend/src/App/ConnectionLostModalConnector.js b/frontend/src/App/ConnectionLostModalConnector.js deleted file mode 100644 index 8ab8e3cd0..000000000 --- a/frontend/src/App/ConnectionLostModalConnector.js +++ /dev/null @@ -1,12 +0,0 @@ -import { connect } from 'react-redux'; -import ConnectionLostModal from './ConnectionLostModal'; - -function createMapDispatchToProps(dispatch, props) { - return { - onModalClose() { - location.reload(); - } - }; -} - -export default connect(undefined, createMapDispatchToProps)(ConnectionLostModal); diff --git a/frontend/src/App/State/AppSectionState.ts b/frontend/src/App/State/AppSectionState.ts index cabc39b1c..f89eb25f7 100644 --- a/frontend/src/App/State/AppSectionState.ts +++ b/frontend/src/App/State/AppSectionState.ts @@ -1,5 +1,6 @@ +import Column from 'Components/Table/Column'; import SortDirection from 'Helpers/Props/SortDirection'; -import { FilterBuilderProp } from './AppState'; +import { FilterBuilderProp, PropertyFilter } from './AppState'; export interface Error { responseJSON: { @@ -18,10 +19,18 @@ export interface AppSectionSaveState { } export interface PagedAppSectionState { + page: number; pageSize: number; + totalPages: number; + totalRecords?: number; +} +export interface TableAppSectionState { + columns: Column[]; } export interface AppSectionFilterState { + selectedFilterKey: string; + filters: PropertyFilter[]; filterBuilderProps: FilterBuilderProp[]; } @@ -38,6 +47,7 @@ export interface AppSectionItemState { isFetching: boolean; isPopulated: boolean; error: Error; + pendingChanges: Partial; item: T; } diff --git a/frontend/src/App/State/AppState.ts b/frontend/src/App/State/AppState.ts index 716fd0711..0f0e82c0d 100644 --- a/frontend/src/App/State/AppState.ts +++ b/frontend/src/App/State/AppState.ts @@ -43,6 +43,10 @@ export interface CustomFilter { } export interface AppSectionState { + isConnected: boolean; + isReconnecting: boolean; + version: string; + prevVersion?: string; dimensions: { isSmallScreen: boolean; width: number; diff --git a/frontend/src/App/State/IndexerAppState.ts b/frontend/src/App/State/IndexerAppState.ts index d070986af..4c0145d0d 100644 --- a/frontend/src/App/State/IndexerAppState.ts +++ b/frontend/src/App/State/IndexerAppState.ts @@ -31,6 +31,8 @@ interface IndexerAppState AppSectionDeleteState, AppSectionSaveState { itemMap: Record; + + isTestingAll: boolean; } export type IndexerStatusAppState = AppSectionState; diff --git a/frontend/src/App/State/SettingsAppState.ts b/frontend/src/App/State/SettingsAppState.ts index d4152431c..33c6c936d 100644 --- a/frontend/src/App/State/SettingsAppState.ts +++ b/frontend/src/App/State/SettingsAppState.ts @@ -7,7 +7,8 @@ import { IndexerCategory } from 'Indexer/Indexer'; import Application from 'typings/Application'; import DownloadClient from 'typings/DownloadClient'; import Notification from 'typings/Notification'; -import { UiSettings } from 'typings/UiSettings'; +import General from 'typings/Settings/General'; +import UiSettings from 'typings/Settings/UiSettings'; export interface AppProfileAppState extends AppSectionState, @@ -24,6 +25,12 @@ export interface ApplicationAppState export interface DownloadClientAppState extends AppSectionState, AppSectionDeleteState, + AppSectionSaveState { + isTestingAll: boolean; +} + +export interface GeneralAppState + extends AppSectionItemState, AppSectionSaveState {} export interface IndexerCategoryAppState @@ -41,6 +48,7 @@ interface SettingsAppState { appProfiles: AppProfileAppState; applications: ApplicationAppState; downloadClients: DownloadClientAppState; + general: GeneralAppState; indexerCategories: IndexerCategoryAppState; notifications: NotificationAppState; ui: UiSettingsAppState; diff --git a/frontend/src/App/State/SystemAppState.ts b/frontend/src/App/State/SystemAppState.ts index d43c1d0ee..8bc1b03e2 100644 --- a/frontend/src/App/State/SystemAppState.ts +++ b/frontend/src/App/State/SystemAppState.ts @@ -1,10 +1,19 @@ +import Health from 'typings/Health'; import SystemStatus from 'typings/SystemStatus'; -import { AppSectionItemState } from './AppSectionState'; +import Task from 'typings/Task'; +import Update from 'typings/Update'; +import AppSectionState, { AppSectionItemState } from './AppSectionState'; +export type HealthAppState = AppSectionState; export type SystemStatusAppState = AppSectionItemState; +export type TaskAppState = AppSectionState; +export type UpdateAppState = AppSectionState; interface SystemAppState { + health: HealthAppState; status: SystemStatusAppState; + tasks: TaskAppState; + updates: UpdateAppState; } export default SystemAppState; diff --git a/frontend/src/Components/Chart/StackedBarChart.js b/frontend/src/Components/Chart/StackedBarChart.js index 3cca1ba81..b69fd8e03 100644 --- a/frontend/src/Components/Chart/StackedBarChart.js +++ b/frontend/src/Components/Chart/StackedBarChart.js @@ -46,6 +46,10 @@ class StackedBarChart extends Component { size: 14, family: defaultFontFamily } + }, + tooltip: { + mode: 'index', + position: 'average' } } }, diff --git a/frontend/src/Components/Error/ErrorBoundaryError.tsx b/frontend/src/Components/Error/ErrorBoundaryError.tsx index 022cf5a45..51d286311 100644 --- a/frontend/src/Components/Error/ErrorBoundaryError.tsx +++ b/frontend/src/Components/Error/ErrorBoundaryError.tsx @@ -63,11 +63,7 @@ function ErrorBoundaryError(props: ErrorBoundaryErrorProps) {
{info.componentStack}
)} - { -
- Version: {window.Prowlarr.version} -
- } +
Version: {window.Prowlarr.version}
); diff --git a/frontend/src/Components/Filter/Builder/FilterBuilderRow.js b/frontend/src/Components/Filter/Builder/FilterBuilderRow.js index 51622509b..b02844c61 100644 --- a/frontend/src/Components/Filter/Builder/FilterBuilderRow.js +++ b/frontend/src/Components/Filter/Builder/FilterBuilderRow.js @@ -3,6 +3,7 @@ import React, { Component } from 'react'; import SelectInput from 'Components/Form/SelectInput'; import IconButton from 'Components/Link/IconButton'; import { filterBuilderTypes, filterBuilderValueTypes, icons } from 'Helpers/Props'; +import sortByProp from 'Utilities/Array/sortByProp'; import AppProfileFilterBuilderRowValueConnector from './AppProfileFilterBuilderRowValueConnector'; import BoolFilterBuilderRowValue from './BoolFilterBuilderRowValue'; import CategoryFilterBuilderRowValue from './CategoryFilterBuilderRowValue'; @@ -212,7 +213,7 @@ class FilterBuilderRow extends Component { key: name, value: typeof label === 'function' ? label() : label }; - }).sort((a, b) => a.value.localeCompare(b.value)); + }).sort(sortByProp('value')); const ValueComponent = getRowValueConnector(selectedFilterBuilderProp); diff --git a/frontend/src/Components/Filter/Builder/FilterBuilderRowValueConnector.js b/frontend/src/Components/Filter/Builder/FilterBuilderRowValueConnector.js index a7aed80b6..d1419327a 100644 --- a/frontend/src/Components/Filter/Builder/FilterBuilderRowValueConnector.js +++ b/frontend/src/Components/Filter/Builder/FilterBuilderRowValueConnector.js @@ -3,7 +3,7 @@ import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import { filterBuilderTypes } from 'Helpers/Props'; import * as filterTypes from 'Helpers/Props/filterTypes'; -import sortByName from 'Utilities/Array/sortByName'; +import sortByProp from 'Utilities/Array/sortByProp'; import FilterBuilderRowValue from './FilterBuilderRowValue'; function createTagListSelector() { @@ -38,7 +38,7 @@ function createTagListSelector() { } return acc; - }, []).sort(sortByName); + }, []).sort(sortByProp('name')); } return _.uniqBy(items, 'id'); diff --git a/frontend/src/Components/Filter/CustomFilters/CustomFiltersModalContent.js b/frontend/src/Components/Filter/CustomFilters/CustomFiltersModalContent.js index 28eb91599..99cb6ec5c 100644 --- a/frontend/src/Components/Filter/CustomFilters/CustomFiltersModalContent.js +++ b/frontend/src/Components/Filter/CustomFilters/CustomFiltersModalContent.js @@ -5,6 +5,7 @@ import ModalBody from 'Components/Modal/ModalBody'; import ModalContent from 'Components/Modal/ModalContent'; import ModalFooter from 'Components/Modal/ModalFooter'; import ModalHeader from 'Components/Modal/ModalHeader'; +import sortByProp from 'Utilities/Array/sortByProp'; import translate from 'Utilities/String/translate'; import CustomFilter from './CustomFilter'; import styles from './CustomFiltersModalContent.css'; @@ -31,7 +32,7 @@ function CustomFiltersModalContent(props) { { customFilters - .sort((a, b) => a.label.localeCompare(b.label)) + .sort((a, b) => sortByProp(a, b, 'label')) .map((customFilter) => { return ( includeNoChange, (state, { includeMixed }) => includeMixed, (appProfiles, includeNoChange, includeMixed) => { diff --git a/frontend/src/Components/Form/DownloadClientSelectInputConnector.js b/frontend/src/Components/Form/DownloadClientSelectInputConnector.js index d5bbe4a2f..9cf7a429a 100644 --- a/frontend/src/Components/Form/DownloadClientSelectInputConnector.js +++ b/frontend/src/Components/Form/DownloadClientSelectInputConnector.js @@ -3,7 +3,8 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import { fetchDownloadClients } from 'Store/Actions/settingsActions'; -import sortByName from 'Utilities/Array/sortByName'; +import sortByProp from 'Utilities/Array/sortByProp'; +import translate from 'Utilities/String/translate'; import EnhancedSelectInput from './EnhancedSelectInput'; function createMapStateToProps() { @@ -21,7 +22,7 @@ function createMapStateToProps() { const values = items .filter((downloadClient) => downloadClient.protocol === protocolFilter) - .sort(sortByName) + .sort(sortByProp('name')) .map((downloadClient) => ({ key: downloadClient.id, value: downloadClient.name, @@ -31,7 +32,7 @@ function createMapStateToProps() { if (includeAny) { values.unshift({ key: 0, - value: '(Any)' + value: `(${translate('Any')})` }); } diff --git a/frontend/src/Components/Form/EnhancedSelectInput.js b/frontend/src/Components/Form/EnhancedSelectInput.js index cc4215025..79b1c999c 100644 --- a/frontend/src/Components/Form/EnhancedSelectInput.js +++ b/frontend/src/Components/Form/EnhancedSelectInput.js @@ -20,6 +20,8 @@ import HintedSelectInputSelectedValue from './HintedSelectInputSelectedValue'; import TextInput from './TextInput'; import styles from './EnhancedSelectInput.css'; +const MINIMUM_DISTANCE_FROM_EDGE = 10; + function isArrowKey(keyCode) { return keyCode === keyCodes.UP_ARROW || keyCode === keyCodes.DOWN_ARROW; } @@ -137,18 +139,9 @@ class EnhancedSelectInput extends Component { // Listeners onComputeMaxHeight = (data) => { - const { - top, - bottom - } = data.offsets.reference; - const windowHeight = window.innerHeight; - if ((/^botton/).test(data.placement)) { - data.styles.maxHeight = windowHeight - bottom; - } else { - data.styles.maxHeight = top; - } + data.styles.maxHeight = windowHeight - MINIMUM_DISTANCE_FROM_EDGE; return data; }; @@ -271,26 +264,29 @@ class EnhancedSelectInput extends Component { this.setState({ isOpen: !this.state.isOpen }); }; - onSelect = (value) => { - if (Array.isArray(this.props.value)) { - let newValue = null; - const index = this.props.value.indexOf(value); + onSelect = (newValue) => { + const { name, value, values, onChange } = this.props; + + if (Array.isArray(value)) { + let arrayValue = null; + const index = value.indexOf(newValue); + if (index === -1) { - newValue = this.props.values.map((v) => v.key).filter((v) => (v === value) || this.props.value.includes(v)); + arrayValue = values.map((v) => v.key).filter((v) => (v === newValue) || value.includes(v)); } else { - newValue = [...this.props.value]; - newValue.splice(index, 1); + arrayValue = [...value]; + arrayValue.splice(index, 1); } - this.props.onChange({ - name: this.props.name, - value: newValue + onChange({ + name, + value: arrayValue }); } else { this.setState({ isOpen: false }); - this.props.onChange({ - name: this.props.name, - value + onChange({ + name, + value: newValue }); } }; @@ -457,6 +453,10 @@ class EnhancedSelectInput extends Component { order: 851, enabled: true, fn: this.onComputeMaxHeight + }, + preventOverflow: { + enabled: true, + boundariesElement: 'viewport' } }} > @@ -485,7 +485,7 @@ class EnhancedSelectInput extends Component { values.map((v, index) => { const hasParent = v.parentKey !== undefined; const depth = hasParent ? 1 : 0; - const parentSelected = hasParent && value.includes(v.parentKey); + const parentSelected = hasParent && Array.isArray(value) && value.includes(v.parentKey); return ( {error.errorMessage} + + { + error.detailedDescription ? + } + tooltip={error.detailedDescription} + kind={kinds.INVERSE} + position={tooltipPositions.TOP} + /> : + null + } ); }) @@ -39,6 +53,18 @@ function Form(props) { kind={kinds.WARNING} > {warning.errorMessage} + + { + warning.detailedDescription ? + } + tooltip={warning.detailedDescription} + kind={kinds.INVERSE} + position={tooltipPositions.TOP} + /> : + null + } ); }) diff --git a/frontend/src/Components/Form/FormInputButton.js b/frontend/src/Components/Form/FormInputButton.js deleted file mode 100644 index a7145363a..000000000 --- a/frontend/src/Components/Form/FormInputButton.js +++ /dev/null @@ -1,54 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; -import Button from 'Components/Link/Button'; -import SpinnerButton from 'Components/Link/SpinnerButton'; -import { kinds } from 'Helpers/Props'; -import styles from './FormInputButton.css'; - -function FormInputButton(props) { - const { - className, - canSpin, - isLastButton, - ...otherProps - } = props; - - if (canSpin) { - return ( - - ); - } - - return ( - - - -
- ); - } -} - -AddIndexerModalContent.propTypes = { - isFetching: PropTypes.bool.isRequired, - isPopulated: PropTypes.bool.isRequired, - error: PropTypes.object, - sortKey: PropTypes.string, - sortDirection: PropTypes.string, - onSortPress: PropTypes.func.isRequired, - indexers: PropTypes.arrayOf(PropTypes.object).isRequired, - onIndexerSelect: PropTypes.func.isRequired, - onModalClose: PropTypes.func.isRequired -}; - -export default AddIndexerModalContent; diff --git a/frontend/src/Indexer/Add/AddIndexerModalContent.tsx b/frontend/src/Indexer/Add/AddIndexerModalContent.tsx new file mode 100644 index 000000000..be1413769 --- /dev/null +++ b/frontend/src/Indexer/Add/AddIndexerModalContent.tsx @@ -0,0 +1,434 @@ +import { some } from 'lodash'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { createSelector } from 'reselect'; +import IndexerAppState from 'App/State/IndexerAppState'; +import Alert from 'Components/Alert'; +import EnhancedSelectInput from 'Components/Form/EnhancedSelectInput'; +import NewznabCategorySelectInputConnector from 'Components/Form/NewznabCategorySelectInputConnector'; +import TextInput from 'Components/Form/TextInput'; +import Button from 'Components/Link/Button'; +import LoadingIndicator from 'Components/Loading/LoadingIndicator'; +import ModalBody from 'Components/Modal/ModalBody'; +import ModalContent from 'Components/Modal/ModalContent'; +import ModalFooter from 'Components/Modal/ModalFooter'; +import ModalHeader from 'Components/Modal/ModalHeader'; +import Scroller from 'Components/Scroller/Scroller'; +import Table from 'Components/Table/Table'; +import TableBody from 'Components/Table/TableBody'; +import { kinds, scrollDirections } from 'Helpers/Props'; +import Indexer, { IndexerCategory } from 'Indexer/Indexer'; +import { + fetchIndexerSchema, + selectIndexerSchema, + setIndexerSchemaSort, +} from 'Store/Actions/indexerActions'; +import createAllIndexersSelector from 'Store/Selectors/createAllIndexersSelector'; +import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector'; +import { SortCallback } from 'typings/callbacks'; +import sortByProp from 'Utilities/Array/sortByProp'; +import getErrorMessage from 'Utilities/Object/getErrorMessage'; +import translate from 'Utilities/String/translate'; +import SelectIndexerRow from './SelectIndexerRow'; +import styles from './AddIndexerModalContent.css'; + +const COLUMNS = [ + { + name: 'protocol', + label: () => translate('Protocol'), + isSortable: true, + isVisible: true, + }, + { + name: 'sortName', + label: () => translate('Name'), + isSortable: true, + isVisible: true, + }, + { + name: 'language', + label: () => translate('Language'), + isSortable: true, + isVisible: true, + }, + { + name: 'description', + label: () => translate('Description'), + isSortable: false, + isVisible: true, + }, + { + name: 'privacy', + label: () => translate('Privacy'), + isSortable: true, + isVisible: true, + }, + { + name: 'categories', + label: () => translate('Categories'), + isSortable: false, + isVisible: true, + }, +]; + +const PROTOCOLS = [ + { + key: 'torrent', + value: 'torrent', + }, + { + key: 'usenet', + value: 'nzb', + }, +]; + +const PRIVACY_LEVELS = [ + { + key: 'private', + get value() { + return translate('Private'); + }, + }, + { + key: 'semiPrivate', + get value() { + return translate('SemiPrivate'); + }, + }, + { + key: 'public', + get value() { + return translate('Public'); + }, + }, +]; + +interface IndexerSchema extends Indexer { + isExistingIndexer: boolean; +} + +function createAddIndexersSelector() { + return createSelector( + createClientSideCollectionSelector('indexers.schema'), + createAllIndexersSelector(), + (indexers: IndexerAppState, allIndexers) => { + const { isFetching, isPopulated, error, items, sortDirection, sortKey } = + indexers; + + const indexerList: IndexerSchema[] = items.map((item) => { + const { definitionName } = item; + return { + ...item, + isExistingIndexer: some(allIndexers, { definitionName }), + }; + }); + + return { + isFetching, + isPopulated, + error, + indexers: indexerList, + sortKey, + sortDirection, + }; + } + ); +} + +interface AddIndexerModalContentProps { + onSelectIndexer(): void; + onModalClose(): void; +} + +function AddIndexerModalContent(props: AddIndexerModalContentProps) { + const { onSelectIndexer, onModalClose } = props; + + const { isFetching, isPopulated, error, indexers, sortKey, sortDirection } = + useSelector(createAddIndexersSelector()); + const dispatch = useDispatch(); + + const [filter, setFilter] = useState(''); + const [filterProtocols, setFilterProtocols] = useState([]); + const [filterLanguages, setFilterLanguages] = useState([]); + const [filterPrivacyLevels, setFilterPrivacyLevels] = useState([]); + const [filterCategories, setFilterCategories] = useState([]); + + useEffect( + () => { + dispatch(fetchIndexerSchema()); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + + const onFilterChange = useCallback( + ({ value }: { value: string }) => { + setFilter(value); + }, + [setFilter] + ); + + const onFilterProtocolsChange = useCallback( + ({ value }: { value: string[] }) => { + setFilterProtocols(value); + }, + [setFilterProtocols] + ); + + const onFilterLanguagesChange = useCallback( + ({ value }: { value: string[] }) => { + setFilterLanguages(value); + }, + [setFilterLanguages] + ); + + const onFilterPrivacyLevelsChange = useCallback( + ({ value }: { value: string[] }) => { + setFilterPrivacyLevels(value); + }, + [setFilterPrivacyLevels] + ); + + const onFilterCategoriesChange = useCallback( + ({ value }: { value: number[] }) => { + setFilterCategories(value); + }, + [setFilterCategories] + ); + + const onIndexerSelect = useCallback( + ({ + implementation, + implementationName, + name, + }: { + implementation: string; + implementationName: string; + name: string; + }) => { + dispatch( + selectIndexerSchema({ + implementation, + implementationName, + name, + }) + ); + + onSelectIndexer(); + }, + [dispatch, onSelectIndexer] + ); + + const onSortPress = useCallback( + (sortKey, sortDirection) => { + dispatch(setIndexerSchemaSort({ sortKey, sortDirection })); + }, + [dispatch] + ); + + const languages = useMemo( + () => + Array.from(new Set(indexers.map(({ language }) => language))) + .map((language) => ({ key: language, value: language })) + .sort(sortByProp('value')), + [indexers] + ); + + const filteredIndexers = useMemo(() => { + const flat = ({ + id, + subCategories = [], + }: { + id: number; + subCategories: IndexerCategory[]; + }): number[] => [id, ...subCategories.flatMap(flat)]; + + return indexers.filter((indexer) => { + if ( + filter.length && + !indexer.name.toLowerCase().includes(filter.toLocaleLowerCase()) && + !indexer.description.toLowerCase().includes(filter.toLocaleLowerCase()) + ) { + return false; + } + + if ( + filterProtocols.length && + !filterProtocols.includes(indexer.protocol) + ) { + return false; + } + + if ( + filterLanguages.length && + !filterLanguages.includes(indexer.language) + ) { + return false; + } + + if ( + filterPrivacyLevels.length && + !filterPrivacyLevels.includes(indexer.privacy) + ) { + return false; + } + + if (filterCategories.length) { + const { categories = [] } = indexer.capabilities || {}; + + const flatCategories = categories + .filter((item) => item.id < 100000) + .flatMap(flat); + + if ( + !filterCategories.every((categoryId) => + flatCategories.includes(categoryId) + ) + ) { + return false; + } + } + + return true; + }); + }, [ + indexers, + filter, + filterProtocols, + filterLanguages, + filterPrivacyLevels, + filterCategories, + ]); + + const errorMessage = getErrorMessage( + error, + translate('UnableToLoadIndexers') + ); + + return ( + + {translate('AddIndexer')} + + + + +
+
+ + + +
+ +
+ + + +
+ +
+ + +
+ +
+ + + +
+
+ + +
{translate('ProwlarrSupportsAnyIndexer')}
+
+ + + {isFetching ? : null} + + {error ? ( + + {errorMessage} + + ) : null} + + {isPopulated && !!indexers.length ? ( + + + {filteredIndexers.map((indexer) => ( + + ))} + +
+ ) : null} + + {isPopulated && !!indexers.length && !filteredIndexers.length ? ( + + {translate('NoIndexersFound')} + + ) : null} +
+
+ + +
+ {isPopulated + ? translate('CountIndexersAvailable', { + count: filteredIndexers.length, + }) + : null} +
+ +
+ +
+
+
+ ); +} + +export default AddIndexerModalContent; diff --git a/frontend/src/Indexer/Add/AddIndexerModalContentConnector.js b/frontend/src/Indexer/Add/AddIndexerModalContentConnector.js deleted file mode 100644 index a422e0a03..000000000 --- a/frontend/src/Indexer/Add/AddIndexerModalContentConnector.js +++ /dev/null @@ -1,94 +0,0 @@ -import { some } from 'lodash'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { fetchIndexerSchema, selectIndexerSchema, setIndexerSchemaSort } from 'Store/Actions/indexerActions'; -import createAllIndexersSelector from 'Store/Selectors/createAllIndexersSelector'; -import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector'; -import AddIndexerModalContent from './AddIndexerModalContent'; - -function createMapStateToProps() { - return createSelector( - createClientSideCollectionSelector('indexers.schema'), - createAllIndexersSelector(), - (indexers, allIndexers) => { - const { - isFetching, - isPopulated, - error, - items, - sortDirection, - sortKey - } = indexers; - - const indexerList = items.map((item) => { - const { definitionName } = item; - return { - ...item, - isExistingIndexer: some(allIndexers, { definitionName }) - }; - }); - - return { - isFetching, - isPopulated, - error, - indexers: indexerList, - sortKey, - sortDirection - }; - } - ); -} - -const mapDispatchToProps = { - fetchIndexerSchema, - selectIndexerSchema, - setIndexerSchemaSort -}; - -class AddIndexerModalContentConnector extends Component { - - // - // Lifecycle - - componentDidMount() { - this.props.fetchIndexerSchema(); - } - - // - // Listeners - - onIndexerSelect = ({ implementation, implementationName, name }) => { - this.props.selectIndexerSchema({ implementation, implementationName, name }); - this.props.onSelectIndexer(); - }; - - onSortPress = (sortKey, sortDirection) => { - this.props.setIndexerSchemaSort({ sortKey, sortDirection }); - }; - - // - // Render - - render() { - return ( - - ); - } -} - -AddIndexerModalContentConnector.propTypes = { - fetchIndexerSchema: PropTypes.func.isRequired, - selectIndexerSchema: PropTypes.func.isRequired, - setIndexerSchemaSort: PropTypes.func.isRequired, - onModalClose: PropTypes.func.isRequired, - onSelectIndexer: PropTypes.func.isRequired -}; - -export default connect(createMapStateToProps, mapDispatchToProps)(AddIndexerModalContentConnector); diff --git a/frontend/src/Indexer/Add/SelectIndexerRow.tsx b/frontend/src/Indexer/Add/SelectIndexerRow.tsx index ab6850573..157050e41 100644 --- a/frontend/src/Indexer/Add/SelectIndexerRow.tsx +++ b/frontend/src/Indexer/Add/SelectIndexerRow.tsx @@ -2,18 +2,19 @@ import React, { useCallback } from 'react'; import Icon from 'Components/Icon'; import TableRowCell from 'Components/Table/Cells/TableRowCell'; import TableRowButton from 'Components/Table/TableRowButton'; +import DownloadProtocol from 'DownloadClient/DownloadProtocol'; import { icons } from 'Helpers/Props'; import CapabilitiesLabel from 'Indexer/Index/Table/CapabilitiesLabel'; +import PrivacyLabel from 'Indexer/Index/Table/PrivacyLabel'; import ProtocolLabel from 'Indexer/Index/Table/ProtocolLabel'; -import { IndexerCapabilities } from 'Indexer/Indexer'; -import firstCharToUpper from 'Utilities/String/firstCharToUpper'; +import { IndexerCapabilities, IndexerPrivacy } from 'Indexer/Indexer'; import translate from 'Utilities/String/translate'; import styles from './SelectIndexerRow.css'; interface SelectIndexerRowProps { name: string; - protocol: string; - privacy: string; + protocol: DownloadProtocol; + privacy: IndexerPrivacy; language: string; description: string; capabilities: IndexerCapabilities; @@ -63,7 +64,9 @@ function SelectIndexerRow(props: SelectIndexerRowProps) { {description} - {translate(firstCharToUpper(privacy))} + + + diff --git a/frontend/src/Indexer/Index/IndexerIndex.tsx b/frontend/src/Indexer/Index/IndexerIndex.tsx index 3407aa05c..e20e269f8 100644 --- a/frontend/src/Indexer/Index/IndexerIndex.tsx +++ b/frontend/src/Indexer/Index/IndexerIndex.tsx @@ -1,4 +1,10 @@ -import React, { useCallback, useMemo, useRef, useState } from 'react'; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { SelectProvider } from 'App/SelectContext'; import ClientSideCollectionAppState from 'App/State/ClientSideCollectionAppState'; @@ -22,12 +28,17 @@ import AddIndexerModal from 'Indexer/Add/AddIndexerModal'; import EditIndexerModalConnector from 'Indexer/Edit/EditIndexerModalConnector'; import NoIndexer from 'Indexer/NoIndexer'; import { executeCommand } from 'Store/Actions/commandActions'; -import { cloneIndexer, testAllIndexers } from 'Store/Actions/indexerActions'; +import { + cloneIndexer, + fetchIndexers, + testAllIndexers, +} from 'Store/Actions/indexerActions'; import { setIndexerFilter, setIndexerSort, setIndexerTableOption, } from 'Store/Actions/indexerIndexActions'; +import { fetchIndexerStatus } from 'Store/Actions/indexerStatusActions'; import scrollPositions from 'Store/scrollPositions'; import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector'; import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector'; @@ -82,6 +93,11 @@ const IndexerIndex = withScrollPosition((props: IndexerIndexProps) => { ); const [isSelectMode, setIsSelectMode] = useState(false); + useEffect(() => { + dispatch(fetchIndexers()); + dispatch(fetchIndexerStatus()); + }, [dispatch]); + const onAddIndexerPress = useCallback(() => { setIsAddIndexerModalOpen(true); }, [setIsAddIndexerModalOpen]); diff --git a/frontend/src/Indexer/Index/Select/Edit/EditIndexerModalContent.tsx b/frontend/src/Indexer/Index/Select/Edit/EditIndexerModalContent.tsx index 2ffbfebfe..9d42aa389 100644 --- a/frontend/src/Indexer/Index/Select/Edit/EditIndexerModalContent.tsx +++ b/frontend/src/Indexer/Index/Select/Edit/EditIndexerModalContent.tsx @@ -19,6 +19,7 @@ interface SavePayload { seedRatio?: number; seedTime?: number; packSeedTime?: number; + preferMagnetUrl?: boolean; } interface EditIndexerModalContentProps { @@ -65,6 +66,9 @@ function EditIndexerModalContent(props: EditIndexerModalContentProps) { const [packSeedTime, setPackSeedTime] = useState( null ); + const [preferMagnetUrl, setPreferMagnetUrl] = useState< + null | string | boolean + >(null); const save = useCallback(() => { let hasChanges = false; @@ -105,6 +109,11 @@ function EditIndexerModalContent(props: EditIndexerModalContentProps) { payload.packSeedTime = packSeedTime as number; } + if (preferMagnetUrl !== null) { + hasChanges = true; + payload.preferMagnetUrl = preferMagnetUrl === 'true'; + } + if (hasChanges) { onSavePress(payload); } @@ -118,6 +127,7 @@ function EditIndexerModalContent(props: EditIndexerModalContentProps) { seedRatio, seedTime, packSeedTime, + preferMagnetUrl, onSavePress, onModalClose, ]); @@ -146,6 +156,9 @@ function EditIndexerModalContent(props: EditIndexerModalContentProps) { case 'packSeedTime': setPackSeedTime(value); break; + case 'preferMagnetUrl': + setPreferMagnetUrl(value); + break; default: console.warn(`EditIndexersModalContent Unknown Input: '${name}'`); } @@ -254,6 +267,18 @@ function EditIndexerModalContent(props: EditIndexerModalContentProps) { onChange={onInputChange} /> + + + {translate('PreferMagnetUrl')} + + + diff --git a/frontend/src/Indexer/Index/Table/CapabilitiesLabel.tsx b/frontend/src/Indexer/Index/Table/CapabilitiesLabel.tsx index c832806ed..8e30532cc 100644 --- a/frontend/src/Indexer/Index/Table/CapabilitiesLabel.tsx +++ b/frontend/src/Indexer/Index/Table/CapabilitiesLabel.tsx @@ -2,6 +2,7 @@ import { uniqBy } from 'lodash'; import React from 'react'; import Label from 'Components/Label'; import { IndexerCapabilities } from 'Indexer/Indexer'; +import translate from 'Utilities/String/translate'; interface CapabilitiesLabelProps { capabilities: IndexerCapabilities; @@ -38,7 +39,7 @@ function CapabilitiesLabel(props: CapabilitiesLabelProps) { ); })} - {filteredList.length === 0 ? : null} + {filteredList.length === 0 ? : null} ); } diff --git a/frontend/src/Indexer/Index/Table/IndexerIndexRow.css b/frontend/src/Indexer/Index/Table/IndexerIndexRow.css index a09d0218e..a20efded3 100644 --- a/frontend/src/Indexer/Index/Table/IndexerIndexRow.css +++ b/frontend/src/Indexer/Index/Table/IndexerIndexRow.css @@ -29,7 +29,8 @@ .minimumSeeders, .seedRatio, .seedTime, -.packSeedTime { +.packSeedTime, +.preferMagnetUrl { composes: cell; flex: 0 0 90px; diff --git a/frontend/src/Indexer/Index/Table/IndexerIndexRow.css.d.ts b/frontend/src/Indexer/Index/Table/IndexerIndexRow.css.d.ts index 5feb0c35d..42821bd74 100644 --- a/frontend/src/Indexer/Index/Table/IndexerIndexRow.css.d.ts +++ b/frontend/src/Indexer/Index/Table/IndexerIndexRow.css.d.ts @@ -11,6 +11,7 @@ interface CssExports { 'id': string; 'minimumSeeders': string; 'packSeedTime': string; + 'preferMagnetUrl': string; 'priority': string; 'privacy': string; 'protocol': string; diff --git a/frontend/src/Indexer/Index/Table/IndexerIndexRow.tsx b/frontend/src/Indexer/Index/Table/IndexerIndexRow.tsx index 9e83e9b8d..e4c3cd32e 100644 --- a/frontend/src/Indexer/Index/Table/IndexerIndexRow.tsx +++ b/frontend/src/Indexer/Index/Table/IndexerIndexRow.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useState } from 'react'; import { useSelector } from 'react-redux'; import { useSelect } from 'App/SelectContext'; -import Label from 'Components/Label'; +import CheckInput from 'Components/Form/CheckInput'; import IconButton from 'Components/Link/IconButton'; import RelativeDateCell from 'Components/Table/Cells/RelativeDateCell'; import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell'; @@ -15,10 +15,10 @@ import createIndexerIndexItemSelector from 'Indexer/Index/createIndexerIndexItem import Indexer from 'Indexer/Indexer'; import IndexerTitleLink from 'Indexer/IndexerTitleLink'; import { SelectStateInputProps } from 'typings/props'; -import firstCharToUpper from 'Utilities/String/firstCharToUpper'; import translate from 'Utilities/String/translate'; import CapabilitiesLabel from './CapabilitiesLabel'; import IndexerStatusCell from './IndexerStatusCell'; +import PrivacyLabel from './PrivacyLabel'; import ProtocolLabel from './ProtocolLabel'; import styles from './IndexerIndexRow.css'; @@ -75,6 +75,10 @@ function IndexerIndexRow(props: IndexerIndexRowProps) { fields.find((field) => field.name === 'torrentBaseSettings.packSeedTime') ?.value ?? undefined; + const preferMagnetUrl = + fields.find((field) => field.name === 'torrentBaseSettings.preferMagnetUrl') + ?.value ?? undefined; + const rssUrl = `${window.location.origin}${ window.Prowlarr.urlBase }/${id}/api?apikey=${encodeURIComponent( @@ -103,6 +107,10 @@ function IndexerIndexRow(props: IndexerIndexRowProps) { setIsDeleteIndexerModalOpen(false); }, [setIsDeleteIndexerModalOpen]); + const checkInputCallback = useCallback(() => { + // Mock handler to satisfy `onChange` being required for `CheckInput`. + }, []); + const onSelectedChange = useCallback( ({ id, value, shiftKey }: SelectStateInputProps) => { selectDispatch({ @@ -175,7 +183,7 @@ function IndexerIndexRow(props: IndexerIndexRowProps) { if (name === 'privacy') { return ( - + ); } @@ -278,6 +286,21 @@ function IndexerIndexRow(props: IndexerIndexRowProps) { ); } + if (name === 'preferMagnetUrl') { + return ( + + {preferMagnetUrl === undefined ? null : ( + + )} + + ); + } + if (name === 'actions') { return ( columns ); -const Row: React.FC> = ({ - index, - style, - data, -}) => { +function Row({ index, style, data }: ListChildComponentProps) { const { items, sortKey, columns, isSelectMode, onCloneIndexerPress } = data; if (index >= items.length) { @@ -77,7 +73,7 @@ const Row: React.FC> = ({ /> ); -}; +} function getWindowScrollTopPosition() { return document.documentElement.scrollTop || document.body.scrollTop || 0; diff --git a/frontend/src/Indexer/Index/Table/IndexerIndexTableHeader.css b/frontend/src/Indexer/Index/Table/IndexerIndexTableHeader.css index 185ba0ef7..839cd49ff 100644 --- a/frontend/src/Indexer/Index/Table/IndexerIndexTableHeader.css +++ b/frontend/src/Indexer/Index/Table/IndexerIndexTableHeader.css @@ -22,7 +22,8 @@ .minimumSeeders, .seedRatio, .seedTime, -.packSeedTime { +.packSeedTime, +.preferMagnetUrl { composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css'; flex: 0 0 90px; diff --git a/frontend/src/Indexer/Index/Table/IndexerIndexTableHeader.css.d.ts b/frontend/src/Indexer/Index/Table/IndexerIndexTableHeader.css.d.ts index f6a54fa25..020d61f27 100644 --- a/frontend/src/Indexer/Index/Table/IndexerIndexTableHeader.css.d.ts +++ b/frontend/src/Indexer/Index/Table/IndexerIndexTableHeader.css.d.ts @@ -8,6 +8,7 @@ interface CssExports { 'id': string; 'minimumSeeders': string; 'packSeedTime': string; + 'preferMagnetUrl': string; 'priority': string; 'privacy': string; 'protocol': string; diff --git a/frontend/src/Indexer/Index/Table/IndexerIndexTableOptions.tsx b/frontend/src/Indexer/Index/Table/IndexerIndexTableOptions.tsx index 6155929df..3aa087790 100644 --- a/frontend/src/Indexer/Index/Table/IndexerIndexTableOptions.tsx +++ b/frontend/src/Indexer/Index/Table/IndexerIndexTableOptions.tsx @@ -1,4 +1,4 @@ -import React, { Fragment, useCallback } from 'react'; +import React, { useCallback } from 'react'; import { useSelector } from 'react-redux'; import FormGroup from 'Components/Form/FormGroup'; import FormInputGroup from 'Components/Form/FormInputGroup'; @@ -32,19 +32,17 @@ function IndexerIndexTableOptions(props: IndexerIndexTableOptionsProps) { ); return ( - - - {translate('ShowSearch')} + + {translate('ShowSearch')} - - - + + ); } diff --git a/frontend/src/Indexer/Index/Table/IndexerStatusCell.tsx b/frontend/src/Indexer/Index/Table/IndexerStatusCell.tsx index a3d694e9d..1a2350302 100644 --- a/frontend/src/Indexer/Index/Table/IndexerStatusCell.tsx +++ b/frontend/src/Indexer/Index/Table/IndexerStatusCell.tsx @@ -8,6 +8,30 @@ import translate from 'Utilities/String/translate'; import DisabledIndexerInfo from './DisabledIndexerInfo'; import styles from './IndexerStatusCell.css'; +function getIconKind(enabled: boolean, redirect: boolean) { + if (enabled) { + return redirect ? kinds.INFO : kinds.SUCCESS; + } + + return kinds.DEFAULT; +} + +function getIconName(enabled: boolean, redirect: boolean) { + if (enabled) { + return redirect ? icons.REDIRECT : icons.CHECK; + } + + return icons.BLOCKLIST; +} + +function getIconTooltip(enabled: boolean, redirect: boolean) { + if (enabled) { + return redirect ? translate('EnabledRedirected') : translate('Enabled'); + } + + return translate('Disabled'); +} + interface IndexerStatusCellProps { className: string; enabled: boolean; @@ -30,22 +54,14 @@ function IndexerStatusCell(props: IndexerStatusCellProps) { ...otherProps } = props; - const enableKind = redirect ? kinds.INFO : kinds.SUCCESS; - const enableIcon = redirect ? icons.REDIRECT : icons.CHECK; - const enableTitle = redirect - ? translate('EnabledRedirected') - : translate('Enabled'); - return ( - { - - } + {status ? ( + {translate(firstCharToUpper(privacy))} + + ); +} + +export default PrivacyLabel; diff --git a/frontend/src/Indexer/Index/Table/ProtocolLabel.css b/frontend/src/Indexer/Index/Table/ProtocolLabel.css index 110c7e01c..c94e383b1 100644 --- a/frontend/src/Indexer/Index/Table/ProtocolLabel.css +++ b/frontend/src/Indexer/Index/Table/ProtocolLabel.css @@ -11,3 +11,7 @@ border-color: var(--usenetColor); background-color: var(--usenetColor); } + +.unknown { + composes: label from '~Components/Label.css'; +} diff --git a/frontend/src/Indexer/Index/Table/ProtocolLabel.css.d.ts b/frontend/src/Indexer/Index/Table/ProtocolLabel.css.d.ts index f3b389e3d..ba0cb260d 100644 --- a/frontend/src/Indexer/Index/Table/ProtocolLabel.css.d.ts +++ b/frontend/src/Indexer/Index/Table/ProtocolLabel.css.d.ts @@ -2,6 +2,7 @@ // Please do not change this file! interface CssExports { 'torrent': string; + 'unknown': string; 'usenet': string; } export const cssExports: CssExports; diff --git a/frontend/src/Indexer/Index/Table/ProtocolLabel.tsx b/frontend/src/Indexer/Index/Table/ProtocolLabel.tsx index 08009109e..c1824452a 100644 --- a/frontend/src/Indexer/Index/Table/ProtocolLabel.tsx +++ b/frontend/src/Indexer/Index/Table/ProtocolLabel.tsx @@ -1,18 +1,15 @@ import React from 'react'; import Label from 'Components/Label'; +import DownloadProtocol from 'DownloadClient/DownloadProtocol'; import styles from './ProtocolLabel.css'; interface ProtocolLabelProps { - protocol: string; + protocol: DownloadProtocol; } -function ProtocolLabel(props: ProtocolLabelProps) { - const { protocol } = props; - +function ProtocolLabel({ protocol }: ProtocolLabelProps) { const protocolName = protocol === 'usenet' ? 'nzb' : protocol; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore ts(7053) return ; } diff --git a/frontend/src/Indexer/Indexer.ts b/frontend/src/Indexer/Indexer.ts index 96a67f446..c363d472c 100644 --- a/frontend/src/Indexer/Indexer.ts +++ b/frontend/src/Indexer/Indexer.ts @@ -1,4 +1,5 @@ import ModelBase from 'App/ModelBase'; +import DownloadProtocol from 'DownloadClient/DownloadProtocol'; export interface IndexerStatus extends ModelBase { disabledTill: Date; @@ -24,6 +25,8 @@ export interface IndexerCapabilities extends ModelBase { categories: IndexerCategory[]; } +export type IndexerPrivacy = 'public' | 'semiPrivate' | 'private'; + export interface IndexerField extends ModelBase { order: number; name: string; @@ -36,6 +39,7 @@ export interface IndexerField extends ModelBase { interface Indexer extends ModelBase { name: string; + definitionName: string; description: string; encoding: string; language: string; @@ -46,8 +50,8 @@ interface Indexer extends ModelBase { supportsSearch: boolean; supportsRedirect: boolean; supportsPagination: boolean; - protocol: string; - privacy: string; + protocol: DownloadProtocol; + privacy: IndexerPrivacy; priority: number; fields: IndexerField[]; tags: number[]; diff --git a/frontend/src/Indexer/Info/History/IndexerHistoryRow.tsx b/frontend/src/Indexer/Info/History/IndexerHistoryRow.tsx index 455b5106d..28d45654c 100644 --- a/frontend/src/Indexer/Info/History/IndexerHistoryRow.tsx +++ b/frontend/src/Indexer/Info/History/IndexerHistoryRow.tsx @@ -68,13 +68,14 @@ function IndexerHistoryRow(props: IndexerHistoryRowProps) { key={parameter.key} title={parameter.title} value={data[parameter.key as keyof HistoryData].toString()} + queryType={data.queryType} /> ); })} - + {data.source ? data.source : null} @@ -83,14 +84,15 @@ function IndexerHistoryRow(props: IndexerHistoryRowProps) { { - return { - indexer, - }; - } - ); -} - -const tabs = ['details', 'categories', 'history', 'stats']; +const TABS = ['details', 'categories', 'history', 'stats']; interface IndexerInfoModalContentProps { indexerId: number; @@ -50,9 +38,7 @@ interface IndexerInfoModalContentProps { } function IndexerInfoModalContent(props: IndexerInfoModalContentProps) { - const { indexerId, onCloneIndexerPress } = props; - - const { indexer } = useSelector(createIndexerInfoItemSelector(indexerId)); + const { indexerId, onModalClose, onCloneIndexerPress } = props; const { id, @@ -64,54 +50,55 @@ function IndexerInfoModalContent(props: IndexerInfoModalContentProps) { fields, tags, protocol, + privacy, capabilities = {} as IndexerCapabilities, - } = indexer as Indexer; + } = useIndexer(indexerId) as Indexer; - const { onModalClose } = props; - - const baseUrl = - fields.find((field) => field.name === 'baseUrl')?.value ?? - (Array.isArray(indexerUrls) ? indexerUrls[0] : undefined); - - const vipExpiration = - fields.find((field) => field.name === 'vipExpiration')?.value ?? undefined; - - const [selectedTab, setSelectedTab] = useState(tabs[0]); + const [selectedTab, setSelectedTab] = useState(TABS[0]); const [isEditIndexerModalOpen, setIsEditIndexerModalOpen] = useState(false); const [isDeleteIndexerModalOpen, setIsDeleteIndexerModalOpen] = useState(false); - const onTabSelect = useCallback( - (index: number) => { - const selectedTab = tabs[index]; + const handleTabSelect = useCallback( + (selectedIndex: number) => { + const selectedTab = TABS[selectedIndex]; setSelectedTab(selectedTab); }, [setSelectedTab] ); - const onEditIndexerPress = useCallback(() => { + const handleEditIndexerPress = useCallback(() => { setIsEditIndexerModalOpen(true); }, [setIsEditIndexerModalOpen]); - const onEditIndexerModalClose = useCallback(() => { + const handleEditIndexerModalClose = useCallback(() => { setIsEditIndexerModalOpen(false); }, [setIsEditIndexerModalOpen]); - const onDeleteIndexerPress = useCallback(() => { + const handleDeleteIndexerPress = useCallback(() => { setIsEditIndexerModalOpen(false); setIsDeleteIndexerModalOpen(true); }, [setIsDeleteIndexerModalOpen]); - const onDeleteIndexerModalClose = useCallback(() => { + const handleDeleteIndexerModalClose = useCallback(() => { setIsDeleteIndexerModalOpen(false); onModalClose(); }, [setIsDeleteIndexerModalOpen, onModalClose]); - const onCloneIndexerPressWrapper = useCallback(() => { + const handleCloneIndexerPressWrapper = useCallback(() => { onCloneIndexerPress(id); onModalClose(); }, [id, onCloneIndexerPress, onModalClose]); + const baseUrl = + fields.find((field) => field.name === 'baseUrl')?.value ?? + (Array.isArray(indexerUrls) ? indexerUrls[0] : undefined); + + const indexerUrl = baseUrl?.replace(/(:\/\/)api\./, '$1'); + + const vipExpiration = + fields.find((field) => field.name === 'vipExpiration')?.value ?? undefined; + return ( {`${name}`} @@ -119,8 +106,8 @@ function IndexerInfoModalContent(props: IndexerInfoModalContentProps) { @@ -160,6 +147,11 @@ function IndexerInfoModalContent(props: IndexerInfoModalContentProps) { title={translate('Language')} data={language ?? '-'} /> + : '-'} + /> {vipExpiration ? ( - {baseUrl ? ( - - {baseUrl.replace(/(:\/\/)api\./, '$1')} - + {indexerUrl ? ( + {indexerUrl} ) : ( '-' )} @@ -358,16 +348,16 @@ function IndexerInfoModalContent(props: IndexerInfoModalContentProps) { -
- +
@@ -375,14 +365,14 @@ function IndexerInfoModalContent(props: IndexerInfoModalContentProps) {
); diff --git a/frontend/src/Indexer/NoIndexer.css b/frontend/src/Indexer/NoIndexer.css index 38a01f391..4ad534de3 100644 --- a/frontend/src/Indexer/NoIndexer.css +++ b/frontend/src/Indexer/NoIndexer.css @@ -1,4 +1,6 @@ .message { + composes: alert from '~Components/Alert.css'; + margin-top: 10px; margin-bottom: 30px; text-align: center; diff --git a/frontend/src/Indexer/NoIndexer.tsx b/frontend/src/Indexer/NoIndexer.tsx index 75650cad6..bf5afa1fe 100644 --- a/frontend/src/Indexer/NoIndexer.tsx +++ b/frontend/src/Indexer/NoIndexer.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import Alert from 'Components/Alert'; import Button from 'Components/Link/Button'; import { kinds } from 'Helpers/Props'; import translate from 'Utilities/String/translate'; @@ -14,11 +15,9 @@ function NoIndexer(props: NoIndexerProps) { if (totalItems > 0) { return ( -
-
- {translate('AllIndexersHiddenDueToFilter')} -
-
+ + {translate('AllIndexersHiddenDueToFilter')} + ); } @@ -29,7 +28,7 @@ function NoIndexer(props: NoIndexerProps) {
-
diff --git a/frontend/src/Indexer/Stats/IndexerStats.tsx b/frontend/src/Indexer/Stats/IndexerStats.tsx index f7b4a9413..bccd49cbe 100644 --- a/frontend/src/Indexer/Stats/IndexerStats.tsx +++ b/frontend/src/Indexer/Stats/IndexerStats.tsx @@ -32,23 +32,30 @@ import IndexerStatsFilterModal from './IndexerStatsFilterModal'; import styles from './IndexerStats.css'; function getAverageResponseTimeData(indexerStats: IndexerStatsIndexer[]) { - const data = indexerStats.map((indexer) => { - return { - label: indexer.indexerName, - value: indexer.averageResponseTime, - }; - }); + const statistics = [...indexerStats].sort((a, b) => + a.averageResponseTime === b.averageResponseTime + ? b.averageGrabResponseTime - a.averageGrabResponseTime + : b.averageResponseTime - a.averageResponseTime + ); - data.sort((a, b) => { - return b.value - a.value; - }); - - return data; + return { + labels: statistics.map((indexer) => indexer.indexerName), + datasets: [ + { + label: translate('AverageQueries'), + data: statistics.map((indexer) => indexer.averageResponseTime), + }, + { + label: translate('AverageGrabs'), + data: statistics.map((indexer) => indexer.averageGrabResponseTime), + }, + ], + }; } function getFailureRateData(indexerStats: IndexerStatsIndexer[]) { - const data = indexerStats.map((indexer) => { - return { + const data = [...indexerStats] + .map((indexer) => ({ label: indexer.indexerName, value: (indexer.numberOfFailedQueries + @@ -59,109 +66,102 @@ function getFailureRateData(indexerStats: IndexerStatsIndexer[]) { indexer.numberOfRssQueries + indexer.numberOfAuthQueries + indexer.numberOfGrabs), - }; - }); + })) + .filter((s) => s.value > 0); - data.sort((a, b) => { - return b.value - a.value; - }); + data.sort((a, b) => b.value - a.value); return data; } function getTotalRequestsData(indexerStats: IndexerStatsIndexer[]) { - const data = { - labels: indexerStats.map((indexer) => indexer.indexerName), + const statistics = [...indexerStats] + .filter( + (s) => + s.numberOfQueries > 0 || + s.numberOfRssQueries > 0 || + s.numberOfAuthQueries > 0 + ) + .sort( + (a, b) => + b.numberOfQueries + + b.numberOfRssQueries + + b.numberOfAuthQueries - + (a.numberOfQueries + a.numberOfRssQueries + a.numberOfAuthQueries) + ); + + return { + labels: statistics.map((indexer) => indexer.indexerName), datasets: [ { label: translate('SearchQueries'), - data: indexerStats.map((indexer) => indexer.numberOfQueries), + data: statistics.map((indexer) => indexer.numberOfQueries), }, { label: translate('RssQueries'), - data: indexerStats.map((indexer) => indexer.numberOfRssQueries), + data: statistics.map((indexer) => indexer.numberOfRssQueries), }, { label: translate('AuthQueries'), - data: indexerStats.map((indexer) => indexer.numberOfAuthQueries), + data: statistics.map((indexer) => indexer.numberOfAuthQueries), }, ], }; - - return data; } function getNumberGrabsData(indexerStats: IndexerStatsIndexer[]) { - const data = indexerStats.map((indexer) => { - return { + const data = [...indexerStats] + .map((indexer) => ({ label: indexer.indexerName, value: indexer.numberOfGrabs - indexer.numberOfFailedGrabs, - }; - }); + })) + .filter((s) => s.value > 0); - data.sort((a, b) => { - return b.value - a.value; - }); + data.sort((a, b) => b.value - a.value); return data; } function getUserAgentGrabsData(indexerStats: IndexerStatsUserAgent[]) { - const data = indexerStats.map((indexer) => { - return { - label: indexer.userAgent ? indexer.userAgent : 'Other', - value: indexer.numberOfGrabs, - }; - }); + const data = indexerStats.map((indexer) => ({ + label: indexer.userAgent ? indexer.userAgent : 'Other', + value: indexer.numberOfGrabs, + })); - data.sort((a, b) => { - return b.value - a.value; - }); + data.sort((a, b) => b.value - a.value); return data; } function getUserAgentQueryData(indexerStats: IndexerStatsUserAgent[]) { - const data = indexerStats.map((indexer) => { - return { - label: indexer.userAgent ? indexer.userAgent : 'Other', - value: indexer.numberOfQueries, - }; - }); + const data = indexerStats.map((indexer) => ({ + label: indexer.userAgent ? indexer.userAgent : 'Other', + value: indexer.numberOfQueries, + })); - data.sort((a, b) => { - return b.value - a.value; - }); + data.sort((a, b) => b.value - a.value); return data; } function getHostGrabsData(indexerStats: IndexerStatsHost[]) { - const data = indexerStats.map((indexer) => { - return { - label: indexer.host ? indexer.host : 'Other', - value: indexer.numberOfGrabs, - }; - }); + const data = indexerStats.map((indexer) => ({ + label: indexer.host ? indexer.host : 'Other', + value: indexer.numberOfGrabs, + })); - data.sort((a, b) => { - return b.value - a.value; - }); + data.sort((a, b) => b.value - a.value); return data; } function getHostQueryData(indexerStats: IndexerStatsHost[]) { - const data = indexerStats.map((indexer) => { - return { - label: indexer.host ? indexer.host : 'Other', - value: indexer.numberOfQueries, - }; - }); + const data = indexerStats.map((indexer) => ({ + label: indexer.host ? indexer.host : 'Other', + value: indexer.numberOfQueries, + })); - data.sort((a, b) => { - return b.value - a.value; - }); + data.sort((a, b) => b.value - a.value); return data; } @@ -241,9 +241,9 @@ function IndexerStats() { selectedFilterKey={selectedFilterKey} filters={filters} customFilters={customFilters} - onFilterSelect={onFilterSelect} filterModalConnectorComponent={IndexerStatsFilterModal} isDisabled={false} + onFilterSelect={onFilterSelect} /> @@ -294,7 +294,7 @@ function IndexerStats() {
- state.indexers.itemMap, + (state: AppState) => state.indexers.items, + (itemMap, allIndexers) => { + return indexerId ? allIndexers[itemMap[indexerId]] : undefined; + } + ); +} + +function useIndexer(indexerId?: number) { + return useSelector(createIndexerSelector(indexerId)); +} + +export default useIndexer; diff --git a/frontend/src/Search/Mobile/SearchIndexOverview.tsx b/frontend/src/Search/Mobile/SearchIndexOverview.tsx index 8d0e6ebff..21a42d70c 100644 --- a/frontend/src/Search/Mobile/SearchIndexOverview.tsx +++ b/frontend/src/Search/Mobile/SearchIndexOverview.tsx @@ -231,7 +231,9 @@ function SearchIndexOverview(props: SearchIndexOverviewProps) { {indexerFlags.length ? indexerFlags - .sort((a, b) => a.localeCompare(b)) + .sort((a, b) => + a.localeCompare(b, undefined, { numeric: true }) + ) .map((flag, index) => { return (
{ - applicationUrl ? + enable && applicationUrl ? { return { diff --git a/frontend/src/Settings/Applications/Applications/Manage/ManageApplicationsModalContent.tsx b/frontend/src/Settings/Applications/Applications/Manage/ManageApplicationsModalContent.tsx index e2c36529c..bb81729f3 100644 --- a/frontend/src/Settings/Applications/Applications/Manage/ManageApplicationsModalContent.tsx +++ b/frontend/src/Settings/Applications/Applications/Manage/ManageApplicationsModalContent.tsx @@ -213,9 +213,9 @@ function ManageApplicationsModalContent( selectAll={true} allSelected={allSelected} allUnselected={allUnselected} - onSelectAllChange={onSelectAllChange} sortKey={sortKey} sortDirection={sortDirection} + onSelectAllChange={onSelectAllChange} onSortPress={onSortPress} > @@ -268,9 +268,9 @@ function ManageApplicationsModalContent( downloadClients ); } diff --git a/frontend/src/Settings/DownloadClients/DownloadClients/Manage/ManageDownloadClientsModalContent.tsx b/frontend/src/Settings/DownloadClients/DownloadClients/Manage/ManageDownloadClientsModalContent.tsx index 4d459d71d..fa82d61b9 100644 --- a/frontend/src/Settings/DownloadClients/DownloadClients/Manage/ManageDownloadClientsModalContent.tsx +++ b/frontend/src/Settings/DownloadClients/DownloadClients/Manage/ManageDownloadClientsModalContent.tsx @@ -186,9 +186,9 @@ function ManageDownloadClientsModalContent( selectAll={true} allSelected={allSelected} allUnselected={allUnselected} - onSelectAllChange={onSelectAllChange} sortKey={sortKey} sortDirection={sortDirection} + onSelectAllChange={onSelectAllChange} onSortPress={onSortPress} > @@ -233,9 +233,9 @@ function ManageDownloadClientsModalContent( diff --git a/frontend/src/Settings/General/LoggingSettings.js b/frontend/src/Settings/General/LoggingSettings.js index 540e29b01..61a259258 100644 --- a/frontend/src/Settings/General/LoggingSettings.js +++ b/frontend/src/Settings/General/LoggingSettings.js @@ -15,12 +15,14 @@ const logLevelOptions = [ function LoggingSettings(props) { const { + advancedSettings, settings, onInputChange } = props; const { - logLevel + logLevel, + logSizeLimit } = settings; return ( @@ -37,11 +39,30 @@ function LoggingSettings(props) { {...logLevel} /> + + + {translate('LogSizeLimit')} + + + ); } LoggingSettings.propTypes = { + advancedSettings: PropTypes.bool.isRequired, settings: PropTypes.object.isRequired, onInputChange: PropTypes.func.isRequired }; diff --git a/frontend/src/Settings/General/UpdateSettings.js b/frontend/src/Settings/General/UpdateSettings.js index 3bf8d43b6..9cf1b7932 100644 --- a/frontend/src/Settings/General/UpdateSettings.js +++ b/frontend/src/Settings/General/UpdateSettings.js @@ -12,7 +12,6 @@ function UpdateSettings(props) { const { advancedSettings, settings, - isWindows, packageUpdateMechanism, onInputChange } = props; @@ -38,10 +37,10 @@ function UpdateSettings(props) { value: titleCase(packageUpdateMechanism) }); } else { - updateOptions.push({ key: 'builtIn', value: 'Built-In' }); + updateOptions.push({ key: 'builtIn', value: translate('BuiltIn') }); } - updateOptions.push({ key: 'script', value: 'Script' }); + updateOptions.push({ key: 'script', value: translate('Script') }); return (
@@ -62,61 +61,58 @@ function UpdateSettings(props) { /> - { - !isWindows && -
- - {translate('Automatic')} +
+ + {translate('Automatic')} - - + + + + {translate('Mechanism')} + + + + + { + updateMechanism.value === 'script' && - {translate('Mechanism')} + {translate('ScriptPath')} - - { - updateMechanism.value === 'script' && - - {translate('ScriptPath')} - - - - } -
- } + } +
); } diff --git a/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxiesConnector.js b/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxiesConnector.js index 9d2188a7c..0d2acae87 100644 --- a/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxiesConnector.js +++ b/frontend/src/Settings/Indexers/IndexerProxies/IndexerProxiesConnector.js @@ -5,13 +5,13 @@ import { createSelector } from 'reselect'; import { deleteIndexerProxy, fetchIndexerProxies } from 'Store/Actions/settingsActions'; import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector'; import createTagsSelector from 'Store/Selectors/createTagsSelector'; -import sortByName from 'Utilities/Array/sortByName'; +import sortByProp from 'Utilities/Array/sortByProp'; import IndexerProxies from './IndexerProxies'; function createMapStateToProps() { return createSelector( - createSortedSectionSelector('settings.indexerProxies', sortByName), - createSortedSectionSelector('indexers', sortByName), + createSortedSectionSelector('settings.indexerProxies', sortByProp('name')), + createSortedSectionSelector('indexers', sortByProp('name')), createTagsSelector(), (indexerProxies, indexers, tagList) => { return { diff --git a/frontend/src/Settings/Notifications/Notifications/NotificationsConnector.js b/frontend/src/Settings/Notifications/Notifications/NotificationsConnector.js index b306f742a..6351c6f8a 100644 --- a/frontend/src/Settings/Notifications/Notifications/NotificationsConnector.js +++ b/frontend/src/Settings/Notifications/Notifications/NotificationsConnector.js @@ -5,12 +5,12 @@ import { createSelector } from 'reselect'; import { deleteNotification, fetchNotifications } from 'Store/Actions/settingsActions'; import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector'; import createTagsSelector from 'Store/Selectors/createTagsSelector'; -import sortByName from 'Utilities/Array/sortByName'; +import sortByProp from 'Utilities/Array/sortByProp'; import Notifications from './Notifications'; function createMapStateToProps() { return createSelector( - createSortedSectionSelector('settings.notifications', sortByName), + createSortedSectionSelector('settings.notifications', sortByProp('name')), createTagsSelector(), (notifications, tagList) => { return { diff --git a/frontend/src/Settings/Profiles/App/AppProfilesConnector.js b/frontend/src/Settings/Profiles/App/AppProfilesConnector.js index a150655a6..02bf845df 100644 --- a/frontend/src/Settings/Profiles/App/AppProfilesConnector.js +++ b/frontend/src/Settings/Profiles/App/AppProfilesConnector.js @@ -4,12 +4,12 @@ import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import { cloneAppProfile, deleteAppProfile, fetchAppProfiles } from 'Store/Actions/settingsActions'; import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector'; -import sortByName from 'Utilities/Array/sortByName'; +import sortByProp from 'Utilities/Array/sortByProp'; import AppProfiles from './AppProfiles'; function createMapStateToProps() { return createSelector( - createSortedSectionSelector('settings.appProfiles', sortByName), + createSortedSectionSelector('settings.appProfiles', sortByProp('name')), (appProfiles) => appProfiles ); } diff --git a/frontend/src/Settings/Tags/TagsConnector.js b/frontend/src/Settings/Tags/TagsConnector.js index b53e2fc0f..1f3de2034 100644 --- a/frontend/src/Settings/Tags/TagsConnector.js +++ b/frontend/src/Settings/Tags/TagsConnector.js @@ -4,11 +4,13 @@ import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import { fetchApplications, fetchIndexerProxies, fetchNotifications } from 'Store/Actions/settingsActions'; import { fetchTagDetails, fetchTags } from 'Store/Actions/tagActions'; +import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector'; +import sortByProp from 'Utilities/Array/sortByProp'; import Tags from './Tags'; function createMapStateToProps() { return createSelector( - (state) => state.tags, + createSortedSectionSelector('tags', sortByProp('label')), (tags) => { const isFetching = tags.isFetching || tags.details.isFetching; const error = tags.error || tags.details.error; diff --git a/frontend/src/Store/Actions/indexerActions.js b/frontend/src/Store/Actions/indexerActions.js index 2aae11b36..e11051c2f 100644 --- a/frontend/src/Store/Actions/indexerActions.js +++ b/frontend/src/Store/Actions/indexerActions.js @@ -3,9 +3,13 @@ import { createAction } from 'redux-actions'; import { filterTypePredicates, sortDirections } from 'Helpers/Props'; import createFetchHandler from 'Store/Actions/Creators/createFetchHandler'; import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHandler'; -import createSaveProviderHandler, { createCancelSaveProviderHandler } from 'Store/Actions/Creators/createSaveProviderHandler'; +import createSaveProviderHandler, { + createCancelSaveProviderHandler +} from 'Store/Actions/Creators/createSaveProviderHandler'; import createTestAllProvidersHandler from 'Store/Actions/Creators/createTestAllProvidersHandler'; -import createTestProviderHandler, { createCancelTestProviderHandler } from 'Store/Actions/Creators/createTestProviderHandler'; +import createTestProviderHandler, { + createCancelTestProviderHandler +} from 'Store/Actions/Creators/createTestProviderHandler'; import createSetProviderFieldValueReducer from 'Store/Actions/Creators/Reducers/createSetProviderFieldValueReducer'; import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer'; import { createThunk, handleThunks } from 'Store/thunks'; @@ -16,6 +20,7 @@ import translate from 'Utilities/String/translate'; import createBulkEditItemHandler from './Creators/createBulkEditItemHandler'; import createBulkRemoveItemHandler from './Creators/createBulkRemoveItemHandler'; import createHandleActions from './Creators/createHandleActions'; +import createClearReducer from './Creators/Reducers/createClearReducer'; import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer'; // @@ -95,11 +100,42 @@ export const filterPredicates = { }; export const sortPredicates = { - vipExpiration: function(item) { - const vipExpiration = - item.fields.find((field) => field.name === 'vipExpiration')?.value ?? ''; + status: function({ enable, redirect }) { + let result = 0; - return vipExpiration; + if (redirect) { + result++; + } + + if (enable) { + result += 2; + } + + return result; + }, + + vipExpiration: function({ fields = [] }) { + return fields.find((field) => field.name === 'vipExpiration')?.value ?? ''; + }, + + minimumSeeders: function({ fields = [] }) { + return fields.find((field) => field.name === 'torrentBaseSettings.appMinimumSeeders')?.value ?? undefined; + }, + + seedRatio: function({ fields = [] }) { + return fields.find((field) => field.name === 'torrentBaseSettings.seedRatio')?.value ?? undefined; + }, + + seedTime: function({ fields = [] }) { + return fields.find((field) => field.name === 'torrentBaseSettings.seedTime')?.value ?? undefined; + }, + + packSeedTime: function({ fields = [] }) { + return fields.find((field) => field.name === 'torrentBaseSettings.packSeedTime')?.value ?? undefined; + }, + + preferMagnetUrl: function({ fields = [] }) { + return fields.find((field) => field.name === 'torrentBaseSettings.preferMagnetUrl')?.value ?? undefined; } }; @@ -110,6 +146,7 @@ export const FETCH_INDEXERS = 'indexers/fetchIndexers'; export const FETCH_INDEXER_SCHEMA = 'indexers/fetchIndexerSchema'; export const SELECT_INDEXER_SCHEMA = 'indexers/selectIndexerSchema'; export const SET_INDEXER_SCHEMA_SORT = 'indexers/setIndexerSchemaSort'; +export const CLEAR_INDEXER_SCHEMA = 'indexers/clearIndexerSchema'; export const CLONE_INDEXER = 'indexers/cloneIndexer'; export const SET_INDEXER_VALUE = 'indexers/setIndexerValue'; export const SET_INDEXER_FIELD_VALUE = 'indexers/setIndexerFieldValue'; @@ -129,6 +166,7 @@ export const fetchIndexers = createThunk(FETCH_INDEXERS); export const fetchIndexerSchema = createThunk(FETCH_INDEXER_SCHEMA); export const selectIndexerSchema = createAction(SELECT_INDEXER_SCHEMA); export const setIndexerSchemaSort = createAction(SET_INDEXER_SCHEMA_SORT); +export const clearIndexerSchema = createAction(CLEAR_INDEXER_SCHEMA); export const cloneIndexer = createAction(CLONE_INDEXER); export const saveIndexer = createThunk(SAVE_INDEXER); @@ -214,6 +252,8 @@ export const reducers = createHandleActions({ }); }, + [CLEAR_INDEXER_SCHEMA]: createClearReducer(schemaSection, defaultState), + [CLONE_INDEXER]: function(state, { payload }) { const id = payload.id; const newState = getSectionState(state, section); diff --git a/frontend/src/Store/Actions/indexerIndexActions.js b/frontend/src/Store/Actions/indexerIndexActions.js index fa4bc3a15..a002d9b41 100644 --- a/frontend/src/Store/Actions/indexerIndexActions.js +++ b/frontend/src/Store/Actions/indexerIndexActions.js @@ -116,6 +116,12 @@ export const defaultState = { isSortable: true, isVisible: false }, + { + name: 'preferMagnetUrl', + label: () => translate('PreferMagnetUrl'), + isSortable: true, + isVisible: false + }, { name: 'tags', label: () => translate('Tags'), diff --git a/frontend/src/Store/Actions/systemActions.js b/frontend/src/Store/Actions/systemActions.js index 92360b589..75d2595cf 100644 --- a/frontend/src/Store/Actions/systemActions.js +++ b/frontend/src/Store/Actions/systemActions.js @@ -110,7 +110,6 @@ export const defaultState = { { name: 'actions', columnLabel: () => translate('Actions'), - isSortable: true, isVisible: true, isModifiable: false } diff --git a/frontend/src/Store/Selectors/createEnabledDownloadClientsSelector.ts b/frontend/src/Store/Selectors/createEnabledDownloadClientsSelector.ts index ac31e5210..3a581587b 100644 --- a/frontend/src/Store/Selectors/createEnabledDownloadClientsSelector.ts +++ b/frontend/src/Store/Selectors/createEnabledDownloadClientsSelector.ts @@ -2,13 +2,17 @@ import { createSelector } from 'reselect'; import { DownloadClientAppState } from 'App/State/SettingsAppState'; import DownloadProtocol from 'DownloadClient/DownloadProtocol'; import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector'; -import sortByName from 'Utilities/Array/sortByName'; +import DownloadClient from 'typings/DownloadClient'; +import sortByProp from 'Utilities/Array/sortByProp'; export default function createEnabledDownloadClientsSelector( protocol: DownloadProtocol ) { return createSelector( - createSortedSectionSelector('settings.downloadClients', sortByName), + createSortedSectionSelector( + 'settings.downloadClients', + sortByProp('name') + ), (downloadClients: DownloadClientAppState) => { const { isFetching, isPopulated, error, items } = downloadClients; diff --git a/frontend/src/Store/Selectors/createSortedSectionSelector.js b/frontend/src/Store/Selectors/createSortedSectionSelector.ts similarity index 68% rename from frontend/src/Store/Selectors/createSortedSectionSelector.js rename to frontend/src/Store/Selectors/createSortedSectionSelector.ts index 331d890c9..abee01f75 100644 --- a/frontend/src/Store/Selectors/createSortedSectionSelector.js +++ b/frontend/src/Store/Selectors/createSortedSectionSelector.ts @@ -1,14 +1,18 @@ import { createSelector } from 'reselect'; import getSectionState from 'Utilities/State/getSectionState'; -function createSortedSectionSelector(section, comparer) { +function createSortedSectionSelector( + section: string, + comparer: (a: T, b: T) => number +) { return createSelector( (state) => state, (state) => { const sectionState = getSectionState(state, section, true); + return { ...sectionState, - items: [...sectionState.items].sort(comparer) + items: [...sectionState.items].sort(comparer), }; } ); diff --git a/frontend/src/System/Backup/BackupRow.js b/frontend/src/System/Backup/BackupRow.js index 5dfc7f940..39f7f1123 100644 --- a/frontend/src/System/Backup/BackupRow.js +++ b/frontend/src/System/Backup/BackupRow.js @@ -116,6 +116,7 @@ class BackupRow extends Component { @@ -138,7 +139,9 @@ class BackupRow extends Component { isOpen={isConfirmDeleteModalOpen} kind={kinds.DANGER} title={translate('DeleteBackup')} - message={translate('DeleteBackupMessageText', { name })} + message={translate('DeleteBackupMessageText', { + name + })} confirmLabel={translate('Delete')} onConfirm={this.onConfirmDeletePress} onCancel={this.onConfirmDeleteModalClose} diff --git a/frontend/src/System/Backup/Backups.js b/frontend/src/System/Backup/Backups.js index 8f7a5b0a5..ede2f97f6 100644 --- a/frontend/src/System/Backup/Backups.js +++ b/frontend/src/System/Backup/Backups.js @@ -109,7 +109,7 @@ class Backups extends Component { { !isFetching && !!error && - {translate('UnableToLoadBackups')} + {translate('BackupsLoadError')} } diff --git a/frontend/src/System/Backup/RestoreBackupModalContent.js b/frontend/src/System/Backup/RestoreBackupModalContent.js index 150c46ad6..9b5daa9f4 100644 --- a/frontend/src/System/Backup/RestoreBackupModalContent.js +++ b/frontend/src/System/Backup/RestoreBackupModalContent.js @@ -14,7 +14,7 @@ import styles from './RestoreBackupModalContent.css'; function getErrorMessage(error) { if (!error || !error.responseJSON || !error.responseJSON.message) { - return 'Error restoring backup'; + return translate('ErrorRestoringBackup'); } return error.responseJSON.message; @@ -146,7 +146,9 @@ class RestoreBackupModalContent extends Component { { - !!id && `Would you like to restore the backup '${name}'?` + !!id && translate('WouldYouLikeToRestoreBackup', { + name + }) } { @@ -203,7 +205,7 @@ class RestoreBackupModalContent extends Component {
- Note: Prowlarr will automatically restart and reload the UI during the restore process. + {translate('RestartReloadNote')}
- } - > - { - isFetching && !isPopulated && - - } - - { - !healthIssues && -
- {translate('HealthNoIssues')} -
- } - - { - healthIssues && - - - { - items.map((item) => { - const internalLink = getInternalLink(item.source); - const testLink = getTestLink(item.source, this.props); - - let kind = kinds.WARNING; - switch (item.type.toLowerCase()) { - case 'error': - kind = kinds.DANGER; - break; - default: - case 'warning': - kind = kinds.WARNING; - break; - case 'notice': - kind = kinds.INFO; - break; - } - - return ( - - - - - - {item.message} - - - - - { - internalLink - } - - { - !!testLink && - testLink - } - - - ); - }) - } - -
- } - - ); - } - -} - -Health.propTypes = { - isFetching: PropTypes.bool.isRequired, - isPopulated: PropTypes.bool.isRequired, - items: PropTypes.array.isRequired, - isTestingAllApplications: PropTypes.bool.isRequired, - isTestingAllDownloadClients: PropTypes.bool.isRequired, - isTestingAllIndexers: PropTypes.bool.isRequired, - dispatchTestAllApplications: PropTypes.func.isRequired, - dispatchTestAllDownloadClients: PropTypes.func.isRequired, - dispatchTestAllIndexers: PropTypes.func.isRequired -}; - -export default Health; diff --git a/frontend/src/System/Status/Health/Health.tsx b/frontend/src/System/Status/Health/Health.tsx new file mode 100644 index 000000000..e0636961b --- /dev/null +++ b/frontend/src/System/Status/Health/Health.tsx @@ -0,0 +1,191 @@ +import React, { useCallback, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import AppState from 'App/State/AppState'; +import Alert from 'Components/Alert'; +import FieldSet from 'Components/FieldSet'; +import Icon from 'Components/Icon'; +import IconButton from 'Components/Link/IconButton'; +import SpinnerIconButton from 'Components/Link/SpinnerIconButton'; +import LoadingIndicator from 'Components/Loading/LoadingIndicator'; +import InlineMarkdown from 'Components/Markdown/InlineMarkdown'; +import TableRowCell from 'Components/Table/Cells/TableRowCell'; +import Column from 'Components/Table/Column'; +import Table from 'Components/Table/Table'; +import TableBody from 'Components/Table/TableBody'; +import TableRow from 'Components/Table/TableRow'; +import { icons, kinds } from 'Helpers/Props'; +import { testAllIndexers } from 'Store/Actions/indexerActions'; +import { + testAllApplications, + testAllDownloadClients, +} from 'Store/Actions/settingsActions'; +import { fetchHealth } from 'Store/Actions/systemActions'; +import titleCase from 'Utilities/String/titleCase'; +import translate from 'Utilities/String/translate'; +import createHealthSelector from './createHealthSelector'; +import HealthItemLink from './HealthItemLink'; +import styles from './Health.css'; + +const columns: Column[] = [ + { + className: styles.status, + name: 'type', + label: '', + isVisible: true, + }, + { + name: 'message', + label: () => translate('Message'), + isVisible: true, + }, + { + name: 'actions', + label: () => translate('Actions'), + isVisible: true, + }, +]; + +function Health() { + const dispatch = useDispatch(); + const { isFetching, isPopulated, items } = useSelector( + createHealthSelector() + ); + const isTestingAllApplications = useSelector( + (state: AppState) => state.settings.applications.isTestingAll + ); + const isTestingAllDownloadClients = useSelector( + (state: AppState) => state.settings.downloadClients.isTestingAll + ); + const isTestingAllIndexers = useSelector( + (state: AppState) => state.indexers.isTestingAll + ); + + const healthIssues = !!items.length; + + const handleTestAllApplicationsPress = useCallback(() => { + dispatch(testAllApplications()); + }, [dispatch]); + + const handleTestAllDownloadClientsPress = useCallback(() => { + dispatch(testAllDownloadClients()); + }, [dispatch]); + + const handleTestAllIndexersPress = useCallback(() => { + dispatch(testAllIndexers()); + }, [dispatch]); + + useEffect(() => { + dispatch(fetchHealth()); + }, [dispatch]); + + return ( +
+ {translate('Health')} + + {isFetching && isPopulated ? ( + + ) : null} + + } + > + {isFetching && !isPopulated ? : null} + + {isPopulated && !healthIssues ? ( +
+ {translate('NoIssuesWithYourConfiguration')} +
+ ) : null} + + {healthIssues ? ( + <> + + + {items.map((item) => { + const source = item.source; + + let kind = kinds.WARNING; + switch (item.type.toLowerCase()) { + case 'error': + kind = kinds.DANGER; + break; + default: + case 'warning': + kind = kinds.WARNING; + break; + case 'notice': + kind = kinds.INFO; + break; + } + + return ( + + + + + + {item.message} + + + + + + + {source === 'ApplicationStatusCheck' || + source === 'ApplicationLongTermStatusCheck' ? ( + + ) : null} + + {source === 'IndexerStatusCheck' || + source === 'IndexerLongTermStatusCheck' ? ( + + ) : null} + + {source === 'DownloadClientStatusCheck' ? ( + + ) : null} + + + ); + })} + +
+ + + + + + ) : null} +
+ ); +} + +export default Health; diff --git a/frontend/src/System/Status/Health/HealthConnector.js b/frontend/src/System/Status/Health/HealthConnector.js deleted file mode 100644 index 687e0ed87..000000000 --- a/frontend/src/System/Status/Health/HealthConnector.js +++ /dev/null @@ -1,74 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { testAllIndexers } from 'Store/Actions/indexerActions'; -import { testAllApplications } from 'Store/Actions/Settings/applications'; -import { testAllDownloadClients } from 'Store/Actions/Settings/downloadClients'; -import { fetchHealth } from 'Store/Actions/systemActions'; -import createHealthCheckSelector from 'Store/Selectors/createHealthCheckSelector'; -import Health from './Health'; - -function createMapStateToProps() { - return createSelector( - createHealthCheckSelector(), - (state) => state.system.health, - (state) => state.settings.applications.isTestingAll, - (state) => state.settings.downloadClients.isTestingAll, - (state) => state.indexers.isTestingAll, - (items, health, isTestingAllApplications, isTestingAllDownloadClients, isTestingAllIndexers) => { - const { - isFetching, - isPopulated - } = health; - - return { - isFetching, - isPopulated, - items, - isTestingAllApplications, - isTestingAllDownloadClients, - isTestingAllIndexers - }; - } - ); -} - -const mapDispatchToProps = { - dispatchFetchHealth: fetchHealth, - dispatchTestAllApplications: testAllApplications, - dispatchTestAllDownloadClients: testAllDownloadClients, - dispatchTestAllIndexers: testAllIndexers -}; - -class HealthConnector extends Component { - - // - // Lifecycle - - componentDidMount() { - this.props.dispatchFetchHealth(); - } - - // - // Render - - render() { - const { - dispatchFetchHealth, - ...otherProps - } = this.props; - - return ( - - ); - } -} - -HealthConnector.propTypes = { - dispatchFetchHealth: PropTypes.func.isRequired -}; - -export default connect(createMapStateToProps, mapDispatchToProps)(HealthConnector); diff --git a/frontend/src/System/Status/Health/HealthItemLink.tsx b/frontend/src/System/Status/Health/HealthItemLink.tsx new file mode 100644 index 000000000..b7a90c783 --- /dev/null +++ b/frontend/src/System/Status/Health/HealthItemLink.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import IconButton from 'Components/Link/IconButton'; +import { icons } from 'Helpers/Props'; +import translate from 'Utilities/String/translate'; + +interface HealthItemLinkProps { + source: string; +} + +function HealthItemLink(props: HealthItemLinkProps) { + const { source } = props; + + switch (source) { + case 'ApplicationStatusCheck': + case 'ApplicationLongTermStatusCheck': + return ( + + ); + case 'DownloadClientStatusCheck': + return ( + + ); + case 'NotificationStatusCheck': + return ( + + ); + case 'IndexerProxyStatusCheck': + return ( + + ); + case 'IndexerRssCheck': + case 'IndexerSearchCheck': + case 'IndexerStatusCheck': + case 'IndexerLongTermStatusCheck': + return ( + + ); + case 'UpdateCheck': + return ( + + ); + default: + return null; + } +} + +export default HealthItemLink; diff --git a/frontend/src/System/Status/Health/HealthStatus.tsx b/frontend/src/System/Status/Health/HealthStatus.tsx new file mode 100644 index 000000000..b12fd3ebb --- /dev/null +++ b/frontend/src/System/Status/Health/HealthStatus.tsx @@ -0,0 +1,56 @@ +import React, { useEffect, useMemo } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import AppState from 'App/State/AppState'; +import PageSidebarStatus from 'Components/Page/Sidebar/PageSidebarStatus'; +import usePrevious from 'Helpers/Hooks/usePrevious'; +import { fetchHealth } from 'Store/Actions/systemActions'; +import createHealthSelector from './createHealthSelector'; + +function HealthStatus() { + const dispatch = useDispatch(); + const { isConnected, isReconnecting } = useSelector( + (state: AppState) => state.app + ); + const { isPopulated, items } = useSelector(createHealthSelector()); + + const wasReconnecting = usePrevious(isReconnecting); + + const { count, errors, warnings } = useMemo(() => { + let errors = false; + let warnings = false; + + items.forEach((item) => { + if (item.type === 'error') { + errors = true; + } + + if (item.type === 'warning') { + warnings = true; + } + }); + + return { + count: items.length, + errors, + warnings, + }; + }, [items]); + + useEffect(() => { + if (!isPopulated) { + dispatch(fetchHealth()); + } + }, [isPopulated, dispatch]); + + useEffect(() => { + if (isConnected && wasReconnecting) { + dispatch(fetchHealth()); + } + }, [isConnected, wasReconnecting, dispatch]); + + return ( + + ); +} + +export default HealthStatus; diff --git a/frontend/src/System/Status/Health/HealthStatusConnector.js b/frontend/src/System/Status/Health/HealthStatusConnector.js deleted file mode 100644 index e609dd712..000000000 --- a/frontend/src/System/Status/Health/HealthStatusConnector.js +++ /dev/null @@ -1,81 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import PageSidebarStatus from 'Components/Page/Sidebar/PageSidebarStatus'; -import { fetchHealth } from 'Store/Actions/systemActions'; -import createHealthCheckSelector from 'Store/Selectors/createHealthCheckSelector'; - -function createMapStateToProps() { - return createSelector( - (state) => state.app, - createHealthCheckSelector(), - (state) => state.system.health, - (app, items, health) => { - const count = items.length; - let errors = false; - let warnings = false; - - items.forEach((item) => { - if (item.type === 'error') { - errors = true; - } - - if (item.type === 'warning') { - warnings = true; - } - }); - - return { - isConnected: app.isConnected, - isReconnecting: app.isReconnecting, - isPopulated: health.isPopulated, - count, - errors, - warnings - }; - } - ); -} - -const mapDispatchToProps = { - fetchHealth -}; - -class HealthStatusConnector extends Component { - - // - // Lifecycle - - componentDidMount() { - if (!this.props.isPopulated) { - this.props.fetchHealth(); - } - } - - componentDidUpdate(prevProps) { - if (this.props.isConnected && prevProps.isReconnecting) { - this.props.fetchHealth(); - } - } - - // - // Render - - render() { - return ( - - ); - } -} - -HealthStatusConnector.propTypes = { - isConnected: PropTypes.bool.isRequired, - isReconnecting: PropTypes.bool.isRequired, - isPopulated: PropTypes.bool.isRequired, - fetchHealth: PropTypes.func.isRequired -}; - -export default connect(createMapStateToProps, mapDispatchToProps)(HealthStatusConnector); diff --git a/frontend/src/System/Status/Health/createHealthSelector.ts b/frontend/src/System/Status/Health/createHealthSelector.ts new file mode 100644 index 000000000..f38e3fe88 --- /dev/null +++ b/frontend/src/System/Status/Health/createHealthSelector.ts @@ -0,0 +1,13 @@ +import { createSelector } from 'reselect'; +import AppState from 'App/State/AppState'; + +function createHealthSelector() { + return createSelector( + (state: AppState) => state.system.health, + (health) => { + return health; + } + ); +} + +export default createHealthSelector; diff --git a/frontend/src/System/Status/MoreInfo/MoreInfo.js b/frontend/src/System/Status/MoreInfo/MoreInfo.js deleted file mode 100644 index dfb23a996..000000000 --- a/frontend/src/System/Status/MoreInfo/MoreInfo.js +++ /dev/null @@ -1,58 +0,0 @@ -import React, { Component } from 'react'; -import DescriptionList from 'Components/DescriptionList/DescriptionList'; -import DescriptionListItemDescription from 'Components/DescriptionList/DescriptionListItemDescription'; -import DescriptionListItemTitle from 'Components/DescriptionList/DescriptionListItemTitle'; -import FieldSet from 'Components/FieldSet'; -import Link from 'Components/Link/Link'; -import translate from 'Utilities/String/translate'; - -class MoreInfo extends Component { - - // - // Render - - render() { - return ( -
- - {translate('HomePage')} - - prowlarr.com - - - {translate('Wiki')} - - wiki.servarr.com/prowlarr - - - {translate('Reddit')} - - r/prowlarr - - - {translate('Discord')} - - prowlarr.com/discord - - - {translate('Source')} - - github.com/Prowlarr/Prowlarr - - - {translate('FeatureRequests')} - - github.com/Prowlarr/Prowlarr/issues - - - -
- ); - } -} - -MoreInfo.propTypes = { - -}; - -export default MoreInfo; diff --git a/frontend/src/System/Status/MoreInfo/MoreInfo.tsx b/frontend/src/System/Status/MoreInfo/MoreInfo.tsx new file mode 100644 index 000000000..928449aed --- /dev/null +++ b/frontend/src/System/Status/MoreInfo/MoreInfo.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import DescriptionList from 'Components/DescriptionList/DescriptionList'; +import DescriptionListItemDescription from 'Components/DescriptionList/DescriptionListItemDescription'; +import DescriptionListItemTitle from 'Components/DescriptionList/DescriptionListItemTitle'; +import FieldSet from 'Components/FieldSet'; +import Link from 'Components/Link/Link'; +import translate from 'Utilities/String/translate'; + +function MoreInfo() { + return ( +
+ + + {translate('HomePage')} + + + prowlarr.com + + + {translate('Wiki')} + + + wiki.servarr.com/prowlarr + + + + + {translate('Reddit')} + + + r/prowlarr + + + + {translate('Discord')} + + + prowlarr.com/discord + + + + {translate('Source')} + + + + github.com/Prowlarr/Prowlarr + + + + + {translate('FeatureRequests')} + + + + github.com/Prowlarr/Prowlarr/issues + + + +
+ ); +} + +export default MoreInfo; diff --git a/frontend/src/System/Status/Status.js b/frontend/src/System/Status/Status.js deleted file mode 100644 index 46e2d0951..000000000 --- a/frontend/src/System/Status/Status.js +++ /dev/null @@ -1,30 +0,0 @@ -import React, { Component } from 'react'; -import PageContent from 'Components/Page/PageContent'; -import PageContentBody from 'Components/Page/PageContentBody'; -import translate from 'Utilities/String/translate'; -import AboutConnector from './About/AboutConnector'; -import Donations from './Donations/Donations'; -import HealthConnector from './Health/HealthConnector'; -import MoreInfo from './MoreInfo/MoreInfo'; - -class Status extends Component { - - // - // Render - - render() { - return ( - - - - - - - - - ); - } - -} - -export default Status; diff --git a/frontend/src/System/Status/Status.tsx b/frontend/src/System/Status/Status.tsx new file mode 100644 index 000000000..6ae088160 --- /dev/null +++ b/frontend/src/System/Status/Status.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import PageContent from 'Components/Page/PageContent'; +import PageContentBody from 'Components/Page/PageContentBody'; +import translate from 'Utilities/String/translate'; +import About from './About/About'; +import Donations from './Donations/Donations'; +import Health from './Health/Health'; +import MoreInfo from './MoreInfo/MoreInfo'; + +function Status() { + return ( + + + + + + + + + ); +} + +export default Status; diff --git a/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.js b/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.js deleted file mode 100644 index acb8c8d36..000000000 --- a/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.js +++ /dev/null @@ -1,203 +0,0 @@ -import moment from 'moment'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import SpinnerIconButton from 'Components/Link/SpinnerIconButton'; -import TableRowCell from 'Components/Table/Cells/TableRowCell'; -import TableRow from 'Components/Table/TableRow'; -import { icons } from 'Helpers/Props'; -import formatDate from 'Utilities/Date/formatDate'; -import formatDateTime from 'Utilities/Date/formatDateTime'; -import formatTimeSpan from 'Utilities/Date/formatTimeSpan'; -import styles from './ScheduledTaskRow.css'; - -function getFormattedDates(props) { - const { - lastExecution, - nextExecution, - interval, - showRelativeDates, - shortDateFormat - } = props; - - const isDisabled = interval === 0; - - if (showRelativeDates) { - return { - lastExecutionTime: moment(lastExecution).fromNow(), - nextExecutionTime: isDisabled ? '-' : moment(nextExecution).fromNow() - }; - } - - return { - lastExecutionTime: formatDate(lastExecution, shortDateFormat), - nextExecutionTime: isDisabled ? '-' : formatDate(nextExecution, shortDateFormat) - }; -} - -class ScheduledTaskRow extends Component { - - // - // Lifecycle - - constructor(props, context) { - super(props, context); - - this.state = getFormattedDates(props); - - this._updateTimeoutId = null; - } - - componentDidMount() { - this.setUpdateTimer(); - } - - componentDidUpdate(prevProps) { - const { - lastExecution, - nextExecution - } = this.props; - - if ( - lastExecution !== prevProps.lastExecution || - nextExecution !== prevProps.nextExecution - ) { - this.setState(getFormattedDates(this.props)); - } - } - - componentWillUnmount() { - if (this._updateTimeoutId) { - this._updateTimeoutId = clearTimeout(this._updateTimeoutId); - } - } - - // - // Listeners - - setUpdateTimer() { - const { interval } = this.props; - const timeout = interval < 60 ? 10000 : 60000; - - this._updateTimeoutId = setTimeout(() => { - this.setState(getFormattedDates(this.props)); - this.setUpdateTimer(); - }, timeout); - } - - // - // Render - - render() { - const { - name, - interval, - lastExecution, - lastStartTime, - lastDuration, - nextExecution, - isQueued, - isExecuting, - longDateFormat, - timeFormat, - onExecutePress - } = this.props; - - const { - lastExecutionTime, - nextExecutionTime - } = this.state; - - const isDisabled = interval === 0; - const executeNow = !isDisabled && moment().isAfter(nextExecution); - const hasNextExecutionTime = !isDisabled && !executeNow; - const duration = moment.duration(interval, 'minutes').humanize().replace(/an?(?=\s)/, '1'); - const hasLastStartTime = moment(lastStartTime).isAfter('2010-01-01'); - - return ( - - {name} - - {isDisabled ? 'disabled' : duration} - - - - {lastExecutionTime} - - - { - !hasLastStartTime && - - - } - - { - hasLastStartTime && - - {formatTimeSpan(lastDuration)} - - } - - { - isDisabled && - - - } - - { - executeNow && isQueued && - queued - } - - { - executeNow && !isQueued && - now - } - - { - hasNextExecutionTime && - - {nextExecutionTime} - - } - - - - - - ); - } -} - -ScheduledTaskRow.propTypes = { - name: PropTypes.string.isRequired, - interval: PropTypes.number.isRequired, - lastExecution: PropTypes.string.isRequired, - lastStartTime: PropTypes.string.isRequired, - lastDuration: PropTypes.string.isRequired, - nextExecution: PropTypes.string.isRequired, - isQueued: PropTypes.bool.isRequired, - isExecuting: PropTypes.bool.isRequired, - showRelativeDates: PropTypes.bool.isRequired, - shortDateFormat: PropTypes.string.isRequired, - longDateFormat: PropTypes.string.isRequired, - timeFormat: PropTypes.string.isRequired, - onExecutePress: PropTypes.func.isRequired -}; - -export default ScheduledTaskRow; diff --git a/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.tsx b/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.tsx new file mode 100644 index 000000000..3a3cd02de --- /dev/null +++ b/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.tsx @@ -0,0 +1,170 @@ +import moment from 'moment'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import SpinnerIconButton from 'Components/Link/SpinnerIconButton'; +import TableRowCell from 'Components/Table/Cells/TableRowCell'; +import TableRow from 'Components/Table/TableRow'; +import usePrevious from 'Helpers/Hooks/usePrevious'; +import { icons } from 'Helpers/Props'; +import { executeCommand } from 'Store/Actions/commandActions'; +import { fetchTask } from 'Store/Actions/systemActions'; +import createCommandSelector from 'Store/Selectors/createCommandSelector'; +import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector'; +import { isCommandExecuting } from 'Utilities/Command'; +import formatDate from 'Utilities/Date/formatDate'; +import formatDateTime from 'Utilities/Date/formatDateTime'; +import formatTimeSpan from 'Utilities/Date/formatTimeSpan'; +import styles from './ScheduledTaskRow.css'; + +interface ScheduledTaskRowProps { + id: number; + taskName: string; + name: string; + interval: number; + lastExecution: string; + lastStartTime: string; + lastDuration: string; + nextExecution: string; +} + +function ScheduledTaskRow(props: ScheduledTaskRowProps) { + const { + id, + taskName, + name, + interval, + lastExecution, + lastStartTime, + lastDuration, + nextExecution, + } = props; + + const dispatch = useDispatch(); + + const { showRelativeDates, longDateFormat, shortDateFormat, timeFormat } = + useSelector(createUISettingsSelector()); + const command = useSelector(createCommandSelector(taskName)); + + const [time, setTime] = useState(Date.now()); + + const isQueued = !!(command && command.status === 'queued'); + const isExecuting = isCommandExecuting(command); + const wasExecuting = usePrevious(isExecuting); + const isDisabled = interval === 0; + const executeNow = !isDisabled && moment().isAfter(nextExecution); + const hasNextExecutionTime = !isDisabled && !executeNow; + const hasLastStartTime = moment(lastStartTime).isAfter('2010-01-01'); + + const duration = useMemo(() => { + return moment + .duration(interval, 'minutes') + .humanize() + .replace(/an?(?=\s)/, '1'); + }, [interval]); + + const { lastExecutionTime, nextExecutionTime } = useMemo(() => { + const isDisabled = interval === 0; + + if (showRelativeDates && time) { + return { + lastExecutionTime: moment(lastExecution).fromNow(), + nextExecutionTime: isDisabled ? '-' : moment(nextExecution).fromNow(), + }; + } + + return { + lastExecutionTime: formatDate(lastExecution, shortDateFormat), + nextExecutionTime: isDisabled + ? '-' + : formatDate(nextExecution, shortDateFormat), + }; + }, [ + time, + interval, + lastExecution, + nextExecution, + showRelativeDates, + shortDateFormat, + ]); + + const handleExecutePress = useCallback(() => { + dispatch( + executeCommand({ + name: taskName, + }) + ); + }, [taskName, dispatch]); + + useEffect(() => { + if (!isExecuting && wasExecuting) { + setTimeout(() => { + dispatch(fetchTask({ id })); + }, 1000); + } + }, [id, isExecuting, wasExecuting, dispatch]); + + useEffect(() => { + const interval = setInterval(() => setTime(Date.now()), 1000); + return () => { + clearInterval(interval); + }; + }, [setTime]); + + return ( + + {name} + + {isDisabled ? 'disabled' : duration} + + + + {lastExecutionTime} + + + {hasLastStartTime ? ( + + {formatTimeSpan(lastDuration)} + + ) : ( + - + )} + + {isDisabled ? ( + - + ) : null} + + {executeNow && isQueued ? ( + queued + ) : null} + + {executeNow && !isQueued ? ( + now + ) : null} + + {hasNextExecutionTime ? ( + + {nextExecutionTime} + + ) : null} + + + + + + ); +} + +export default ScheduledTaskRow; diff --git a/frontend/src/System/Tasks/Scheduled/ScheduledTaskRowConnector.js b/frontend/src/System/Tasks/Scheduled/ScheduledTaskRowConnector.js deleted file mode 100644 index dae790d68..000000000 --- a/frontend/src/System/Tasks/Scheduled/ScheduledTaskRowConnector.js +++ /dev/null @@ -1,92 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { executeCommand } from 'Store/Actions/commandActions'; -import { fetchTask } from 'Store/Actions/systemActions'; -import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; -import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector'; -import { findCommand, isCommandExecuting } from 'Utilities/Command'; -import ScheduledTaskRow from './ScheduledTaskRow'; - -function createMapStateToProps() { - return createSelector( - (state, { taskName }) => taskName, - createCommandsSelector(), - createUISettingsSelector(), - (taskName, commands, uiSettings) => { - const command = findCommand(commands, { name: taskName }); - - return { - isQueued: !!(command && command.state === 'queued'), - isExecuting: isCommandExecuting(command), - showRelativeDates: uiSettings.showRelativeDates, - shortDateFormat: uiSettings.shortDateFormat, - longDateFormat: uiSettings.longDateFormat, - timeFormat: uiSettings.timeFormat - }; - } - ); -} - -function createMapDispatchToProps(dispatch, props) { - const taskName = props.taskName; - - return { - dispatchFetchTask() { - dispatch(fetchTask({ - id: props.id - })); - }, - - onExecutePress() { - dispatch(executeCommand({ - name: taskName - })); - } - }; -} - -class ScheduledTaskRowConnector extends Component { - - // - // Lifecycle - - componentDidUpdate(prevProps) { - const { - isExecuting, - dispatchFetchTask - } = this.props; - - if (!isExecuting && prevProps.isExecuting) { - // Give the host a moment to update after the command completes - setTimeout(() => { - dispatchFetchTask(); - }, 1000); - } - } - - // - // Render - - render() { - const { - dispatchFetchTask, - ...otherProps - } = this.props; - - return ( - - ); - } -} - -ScheduledTaskRowConnector.propTypes = { - id: PropTypes.number.isRequired, - isExecuting: PropTypes.bool.isRequired, - dispatchFetchTask: PropTypes.func.isRequired -}; - -export default connect(createMapStateToProps, createMapDispatchToProps)(ScheduledTaskRowConnector); diff --git a/frontend/src/System/Tasks/Scheduled/ScheduledTasks.js b/frontend/src/System/Tasks/Scheduled/ScheduledTasks.js deleted file mode 100644 index bec151613..000000000 --- a/frontend/src/System/Tasks/Scheduled/ScheduledTasks.js +++ /dev/null @@ -1,85 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import FieldSet from 'Components/FieldSet'; -import LoadingIndicator from 'Components/Loading/LoadingIndicator'; -import Table from 'Components/Table/Table'; -import TableBody from 'Components/Table/TableBody'; -import translate from 'Utilities/String/translate'; -import ScheduledTaskRowConnector from './ScheduledTaskRowConnector'; - -const columns = [ - { - name: 'name', - label: () => translate('Name'), - isVisible: true - }, - { - name: 'interval', - label: () => translate('Interval'), - isVisible: true - }, - { - name: 'lastExecution', - label: () => translate('LastExecution'), - isVisible: true - }, - { - name: 'lastDuration', - label: () => translate('LastDuration'), - isVisible: true - }, - { - name: 'nextExecution', - label: () => translate('NextExecution'), - isVisible: true - }, - { - name: 'actions', - isVisible: true - } -]; - -function ScheduledTasks(props) { - const { - isFetching, - isPopulated, - items - } = props; - - return ( -
- { - isFetching && !isPopulated && - - } - - { - isPopulated && - - - { - items.map((item) => { - return ( - - ); - }) - } - -
- } -
- ); -} - -ScheduledTasks.propTypes = { - isFetching: PropTypes.bool.isRequired, - isPopulated: PropTypes.bool.isRequired, - items: PropTypes.array.isRequired -}; - -export default ScheduledTasks; diff --git a/frontend/src/System/Tasks/Scheduled/ScheduledTasks.tsx b/frontend/src/System/Tasks/Scheduled/ScheduledTasks.tsx new file mode 100644 index 000000000..fcf5764bb --- /dev/null +++ b/frontend/src/System/Tasks/Scheduled/ScheduledTasks.tsx @@ -0,0 +1,73 @@ +import React, { useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import AppState from 'App/State/AppState'; +import FieldSet from 'Components/FieldSet'; +import LoadingIndicator from 'Components/Loading/LoadingIndicator'; +import Column from 'Components/Table/Column'; +import Table from 'Components/Table/Table'; +import TableBody from 'Components/Table/TableBody'; +import { fetchTasks } from 'Store/Actions/systemActions'; +import translate from 'Utilities/String/translate'; +import ScheduledTaskRow from './ScheduledTaskRow'; + +const columns: Column[] = [ + { + name: 'name', + label: () => translate('Name'), + isVisible: true, + }, + { + name: 'interval', + label: () => translate('Interval'), + isVisible: true, + }, + { + name: 'lastExecution', + label: () => translate('LastExecution'), + isVisible: true, + }, + { + name: 'lastDuration', + label: () => translate('LastDuration'), + isVisible: true, + }, + { + name: 'nextExecution', + label: () => translate('NextExecution'), + isVisible: true, + }, + { + name: 'actions', + label: '', + isVisible: true, + }, +]; + +function ScheduledTasks() { + const dispatch = useDispatch(); + const { isFetching, isPopulated, items } = useSelector( + (state: AppState) => state.system.tasks + ); + + useEffect(() => { + dispatch(fetchTasks()); + }, [dispatch]); + + return ( +
+ {isFetching && !isPopulated && } + + {isPopulated && ( + + + {items.map((item) => { + return ; + })} + +
+ )} +
+ ); +} + +export default ScheduledTasks; diff --git a/frontend/src/System/Tasks/Scheduled/ScheduledTasksConnector.js b/frontend/src/System/Tasks/Scheduled/ScheduledTasksConnector.js deleted file mode 100644 index 8f418d3bb..000000000 --- a/frontend/src/System/Tasks/Scheduled/ScheduledTasksConnector.js +++ /dev/null @@ -1,46 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { fetchTasks } from 'Store/Actions/systemActions'; -import ScheduledTasks from './ScheduledTasks'; - -function createMapStateToProps() { - return createSelector( - (state) => state.system.tasks, - (tasks) => { - return tasks; - } - ); -} - -const mapDispatchToProps = { - dispatchFetchTasks: fetchTasks -}; - -class ScheduledTasksConnector extends Component { - - // - // Lifecycle - - componentDidMount() { - this.props.dispatchFetchTasks(); - } - - // - // Render - - render() { - return ( - - ); - } -} - -ScheduledTasksConnector.propTypes = { - dispatchFetchTasks: PropTypes.func.isRequired -}; - -export default connect(createMapStateToProps, mapDispatchToProps)(ScheduledTasksConnector); diff --git a/frontend/src/System/Tasks/Tasks.js b/frontend/src/System/Tasks/Tasks.tsx similarity index 79% rename from frontend/src/System/Tasks/Tasks.js rename to frontend/src/System/Tasks/Tasks.tsx index 03a3b6ce4..26473d7ba 100644 --- a/frontend/src/System/Tasks/Tasks.js +++ b/frontend/src/System/Tasks/Tasks.tsx @@ -3,13 +3,13 @@ import PageContent from 'Components/Page/PageContent'; import PageContentBody from 'Components/Page/PageContentBody'; import translate from 'Utilities/String/translate'; import QueuedTasks from './Queued/QueuedTasks'; -import ScheduledTasksConnector from './Scheduled/ScheduledTasksConnector'; +import ScheduledTasks from './Scheduled/ScheduledTasks'; function Tasks() { return ( - + diff --git a/frontend/src/System/Updates/UpdateChanges.js b/frontend/src/System/Updates/UpdateChanges.js deleted file mode 100644 index 9d6b9decc..000000000 --- a/frontend/src/System/Updates/UpdateChanges.js +++ /dev/null @@ -1,50 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import InlineMarkdown from 'Components/Markdown/InlineMarkdown'; -import styles from './UpdateChanges.css'; - -class UpdateChanges extends Component { - - // - // Render - - render() { - const { - title, - changes - } = this.props; - - if (changes.length === 0) { - return null; - } - - return ( -
-
{title}
-
    - { - changes.map((change, index) => { - const checkChange = change.replace(/#\d{3,5}\b/g, (match, contents) => { - return `[${match}](https://github.com/Prowlarr/Prowlarr/issues/${match.substring(1)})`; - }); - - return ( -
  • - -
  • - ); - }) - } -
-
- ); - } - -} - -UpdateChanges.propTypes = { - title: PropTypes.string.isRequired, - changes: PropTypes.arrayOf(PropTypes.string) -}; - -export default UpdateChanges; diff --git a/frontend/src/System/Updates/UpdateChanges.tsx b/frontend/src/System/Updates/UpdateChanges.tsx new file mode 100644 index 000000000..460814cbe --- /dev/null +++ b/frontend/src/System/Updates/UpdateChanges.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import InlineMarkdown from 'Components/Markdown/InlineMarkdown'; +import styles from './UpdateChanges.css'; + +interface UpdateChangesProps { + title: string; + changes: string[]; +} + +function UpdateChanges(props: UpdateChangesProps) { + const { title, changes } = props; + + if (changes.length === 0) { + return null; + } + + const uniqueChanges = [...new Set(changes)]; + + return ( +
+
{title}
+
    + {uniqueChanges.map((change, index) => { + const checkChange = change.replace( + /#\d{3,5}\b/g, + (match) => + `[${match}](https://github.com/Prowlarr/Prowlarr/issues/${match.substring( + 1 + )})` + ); + + return ( +
  • + +
  • + ); + })} +
+
+ ); +} + +export default UpdateChanges; diff --git a/frontend/src/System/Updates/Updates.js b/frontend/src/System/Updates/Updates.js deleted file mode 100644 index 40ab58c75..000000000 --- a/frontend/src/System/Updates/Updates.js +++ /dev/null @@ -1,252 +0,0 @@ -import _ from 'lodash'; -import PropTypes from 'prop-types'; -import React, { Component, Fragment } from 'react'; -import Alert from 'Components/Alert'; -import Icon from 'Components/Icon'; -import Label from 'Components/Label'; -import SpinnerButton from 'Components/Link/SpinnerButton'; -import LoadingIndicator from 'Components/Loading/LoadingIndicator'; -import InlineMarkdown from 'Components/Markdown/InlineMarkdown'; -import PageContent from 'Components/Page/PageContent'; -import PageContentBody from 'Components/Page/PageContentBody'; -import { icons, kinds } from 'Helpers/Props'; -import formatDate from 'Utilities/Date/formatDate'; -import formatDateTime from 'Utilities/Date/formatDateTime'; -import translate from 'Utilities/String/translate'; -import UpdateChanges from './UpdateChanges'; -import styles from './Updates.css'; - -class Updates extends Component { - - // - // Render - - render() { - const { - currentVersion, - isFetching, - isPopulated, - updatesError, - generalSettingsError, - items, - isInstallingUpdate, - updateMechanism, - isDocker, - updateMechanismMessage, - shortDateFormat, - longDateFormat, - timeFormat, - onInstallLatestPress - } = this.props; - - const hasError = !!(updatesError || generalSettingsError); - const hasUpdates = isPopulated && !hasError && items.length > 0; - const noUpdates = isPopulated && !hasError && !items.length; - const hasUpdateToInstall = hasUpdates && _.some(items, { installable: true, latest: true }); - const noUpdateToInstall = hasUpdates && !hasUpdateToInstall; - - const externalUpdaterPrefix = 'Unable to update Prowlarr directly,'; - const externalUpdaterMessages = { - external: 'Prowlarr is configured to use an external update mechanism', - apt: 'use apt to install the update', - docker: 'update the docker container to receive the update' - }; - - return ( - - - { - !isPopulated && !hasError && - - } - - { - noUpdates && - - {translate('NoUpdatesAreAvailable')} - - } - - { - hasUpdateToInstall && -
- { - (updateMechanism === 'builtIn' || updateMechanism === 'script') && !isDocker ? - - Install Latest - : - - - - -
- {externalUpdaterPrefix} -
-
- } - - { - isFetching && - - } -
- } - - { - noUpdateToInstall && -
- - -
- {translate('TheLatestVersionIsAlreadyInstalled')} -
- - { - isFetching && - - } -
- } - - { - hasUpdates && -
- { - items.map((update) => { - const hasChanges = !!update.changes; - - return ( -
-
-
{update.version}
-
-
- {formatDate(update.releaseDate, shortDateFormat)} -
- - { - update.branch === 'master' ? - null: - - } - - { - update.version === currentVersion ? - : - null - } - - { - update.version !== currentVersion && update.installedOn ? - : - null - } -
- - { - !hasChanges && -
- {translate('MaintenanceRelease')} -
- } - - { - hasChanges && -
- - - -
- } -
- ); - }) - } -
- } - - { - !!updatesError && -
- Failed to fetch updates -
- } - - { - !!generalSettingsError && -
- Failed to update settings -
- } -
-
- ); - } - -} - -Updates.propTypes = { - currentVersion: PropTypes.string.isRequired, - isFetching: PropTypes.bool.isRequired, - isPopulated: PropTypes.bool.isRequired, - updatesError: PropTypes.object, - generalSettingsError: PropTypes.object, - items: PropTypes.array.isRequired, - isInstallingUpdate: PropTypes.bool.isRequired, - isDocker: PropTypes.bool.isRequired, - updateMechanism: PropTypes.string, - updateMechanismMessage: PropTypes.string, - shortDateFormat: PropTypes.string.isRequired, - longDateFormat: PropTypes.string.isRequired, - timeFormat: PropTypes.string.isRequired, - onInstallLatestPress: PropTypes.func.isRequired -}; - -export default Updates; diff --git a/frontend/src/System/Updates/Updates.tsx b/frontend/src/System/Updates/Updates.tsx new file mode 100644 index 000000000..ea309a1cc --- /dev/null +++ b/frontend/src/System/Updates/Updates.tsx @@ -0,0 +1,303 @@ +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { createSelector } from 'reselect'; +import AppState from 'App/State/AppState'; +import * as commandNames from 'Commands/commandNames'; +import Alert from 'Components/Alert'; +import Icon from 'Components/Icon'; +import Label from 'Components/Label'; +import SpinnerButton from 'Components/Link/SpinnerButton'; +import LoadingIndicator from 'Components/Loading/LoadingIndicator'; +import InlineMarkdown from 'Components/Markdown/InlineMarkdown'; +import ConfirmModal from 'Components/Modal/ConfirmModal'; +import PageContent from 'Components/Page/PageContent'; +import PageContentBody from 'Components/Page/PageContentBody'; +import { icons, kinds } from 'Helpers/Props'; +import { executeCommand } from 'Store/Actions/commandActions'; +import { fetchGeneralSettings } from 'Store/Actions/settingsActions'; +import { fetchUpdates } from 'Store/Actions/systemActions'; +import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector'; +import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector'; +import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector'; +import { UpdateMechanism } from 'typings/Settings/General'; +import formatDate from 'Utilities/Date/formatDate'; +import formatDateTime from 'Utilities/Date/formatDateTime'; +import translate from 'Utilities/String/translate'; +import UpdateChanges from './UpdateChanges'; +import styles from './Updates.css'; + +const VERSION_REGEX = /\d+\.\d+\.\d+\.\d+/i; + +function createUpdatesSelector() { + return createSelector( + (state: AppState) => state.system.updates, + (state: AppState) => state.settings.general, + (updates, generalSettings) => { + const { error: updatesError, items } = updates; + + const isFetching = updates.isFetching || generalSettings.isFetching; + const isPopulated = updates.isPopulated && generalSettings.isPopulated; + + return { + isFetching, + isPopulated, + updatesError, + generalSettingsError: generalSettings.error, + items, + updateMechanism: generalSettings.item.updateMechanism, + }; + } + ); +} + +function Updates() { + const currentVersion = useSelector((state: AppState) => state.app.version); + const { packageUpdateMechanismMessage } = useSelector( + createSystemStatusSelector() + ); + const { shortDateFormat, longDateFormat, timeFormat } = useSelector( + createUISettingsSelector() + ); + const isInstallingUpdate = useSelector( + createCommandExecutingSelector(commandNames.APPLICATION_UPDATE) + ); + + const { + isFetching, + isPopulated, + updatesError, + generalSettingsError, + items, + updateMechanism, + } = useSelector(createUpdatesSelector()); + + const dispatch = useDispatch(); + const [isMajorUpdateModalOpen, setIsMajorUpdateModalOpen] = useState(false); + const hasError = !!(updatesError || generalSettingsError); + const hasUpdates = isPopulated && !hasError && items.length > 0; + const noUpdates = isPopulated && !hasError && !items.length; + + const externalUpdaterPrefix = translate('UpdateAppDirectlyLoadError'); + const externalUpdaterMessages: Partial> = { + external: translate('ExternalUpdater'), + apt: translate('AptUpdater'), + docker: translate('DockerUpdater'), + }; + + const { isMajorUpdate, hasUpdateToInstall } = useMemo(() => { + const majorVersion = parseInt( + currentVersion.match(VERSION_REGEX)?.[0] ?? '0' + ); + + const latestVersion = items[0]?.version; + const latestMajorVersion = parseInt( + latestVersion?.match(VERSION_REGEX)?.[0] ?? '0' + ); + + return { + isMajorUpdate: latestMajorVersion > majorVersion, + hasUpdateToInstall: items.some( + (update) => update.installable && update.latest + ), + }; + }, [currentVersion, items]); + + const noUpdateToInstall = hasUpdates && !hasUpdateToInstall; + + const handleInstallLatestPress = useCallback(() => { + if (isMajorUpdate) { + setIsMajorUpdateModalOpen(true); + } else { + dispatch(executeCommand({ name: commandNames.APPLICATION_UPDATE })); + } + }, [isMajorUpdate, setIsMajorUpdateModalOpen, dispatch]); + + const handleInstallLatestMajorVersionPress = useCallback(() => { + setIsMajorUpdateModalOpen(false); + + dispatch( + executeCommand({ + name: commandNames.APPLICATION_UPDATE, + installMajorUpdate: true, + }) + ); + }, [setIsMajorUpdateModalOpen, dispatch]); + + const handleCancelMajorVersionPress = useCallback(() => { + setIsMajorUpdateModalOpen(false); + }, [setIsMajorUpdateModalOpen]); + + useEffect(() => { + dispatch(fetchUpdates()); + dispatch(fetchGeneralSettings()); + }, [dispatch]); + + return ( + + + {isPopulated || hasError ? null : } + + {noUpdates ? ( + {translate('NoUpdatesAreAvailable')} + ) : null} + + {hasUpdateToInstall ? ( +
+ {updateMechanism === 'builtIn' || updateMechanism === 'script' ? ( + + {translate('InstallLatest')} + + ) : ( + <> + + +
+ {externalUpdaterPrefix}{' '} + +
+ + )} + + {isFetching ? ( + + ) : null} +
+ ) : null} + + {noUpdateToInstall && ( +
+ +
{translate('OnLatestVersion')}
+ + {isFetching && ( + + )} +
+ )} + + {hasUpdates && ( +
+ {items.map((update) => { + return ( +
+
+
{update.version}
+
+
+ {formatDate(update.releaseDate, shortDateFormat)} +
+ + {update.branch === 'master' ? null : ( + + )} + + {update.version === currentVersion ? ( + + ) : null} + + {update.version !== currentVersion && update.installedOn ? ( + + ) : null} +
+ + {update.changes ? ( +
+ + + +
+ ) : ( +
{translate('MaintenanceRelease')}
+ )} +
+ ); + })} +
+ )} + + {updatesError ? ( + + {translate('FailedToFetchUpdates')} + + ) : null} + + {generalSettingsError ? ( + + {translate('FailedToFetchSettings')} + + ) : null} + + +
{translate('InstallMajorVersionUpdateMessage')}
+
+ +
+ + } + confirmLabel={translate('Install')} + onConfirm={handleInstallLatestMajorVersionPress} + onCancel={handleCancelMajorVersionPress} + /> +
+
+ ); +} + +export default Updates; diff --git a/frontend/src/System/Updates/UpdatesConnector.js b/frontend/src/System/Updates/UpdatesConnector.js deleted file mode 100644 index 38873a990..000000000 --- a/frontend/src/System/Updates/UpdatesConnector.js +++ /dev/null @@ -1,101 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import * as commandNames from 'Commands/commandNames'; -import { executeCommand } from 'Store/Actions/commandActions'; -import { fetchGeneralSettings } from 'Store/Actions/settingsActions'; -import { fetchUpdates } from 'Store/Actions/systemActions'; -import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector'; -import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector'; -import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector'; -import Updates from './Updates'; - -function createMapStateToProps() { - return createSelector( - (state) => state.app.version, - createSystemStatusSelector(), - (state) => state.system.updates, - (state) => state.settings.general, - createUISettingsSelector(), - createSystemStatusSelector(), - createCommandExecutingSelector(commandNames.APPLICATION_UPDATE), - ( - currentVersion, - status, - updates, - generalSettings, - uiSettings, - systemStatus, - isInstallingUpdate - ) => { - const { - error: updatesError, - items - } = updates; - - const isFetching = updates.isFetching || generalSettings.isFetching; - const isPopulated = updates.isPopulated && generalSettings.isPopulated; - - return { - currentVersion, - isFetching, - isPopulated, - updatesError, - generalSettingsError: generalSettings.error, - items, - isInstallingUpdate, - isDocker: systemStatus.isDocker, - updateMechanism: generalSettings.item.updateMechanism, - updateMechanismMessage: status.packageUpdateMechanismMessage, - shortDateFormat: uiSettings.shortDateFormat, - longDateFormat: uiSettings.longDateFormat, - timeFormat: uiSettings.timeFormat - }; - } - ); -} - -const mapDispatchToProps = { - dispatchFetchUpdates: fetchUpdates, - dispatchFetchGeneralSettings: fetchGeneralSettings, - dispatchExecuteCommand: executeCommand -}; - -class UpdatesConnector extends Component { - - // - // Lifecycle - - componentDidMount() { - this.props.dispatchFetchUpdates(); - this.props.dispatchFetchGeneralSettings(); - } - - // - // Listeners - - onInstallLatestPress = () => { - this.props.dispatchExecuteCommand({ name: commandNames.APPLICATION_UPDATE }); - }; - - // - // Render - - render() { - return ( - - ); - } -} - -UpdatesConnector.propTypes = { - dispatchFetchUpdates: PropTypes.func.isRequired, - dispatchFetchGeneralSettings: PropTypes.func.isRequired, - dispatchExecuteCommand: PropTypes.func.isRequired -}; - -export default connect(createMapStateToProps, mapDispatchToProps)(UpdatesConnector); diff --git a/frontend/src/Utilities/Array/sortByName.js b/frontend/src/Utilities/Array/sortByName.js deleted file mode 100644 index 1956d3bac..000000000 --- a/frontend/src/Utilities/Array/sortByName.js +++ /dev/null @@ -1,5 +0,0 @@ -function sortByName(a, b) { - return a.name.localeCompare(b.name); -} - -export default sortByName; diff --git a/frontend/src/Utilities/Array/sortByProp.ts b/frontend/src/Utilities/Array/sortByProp.ts new file mode 100644 index 000000000..8fbde08c9 --- /dev/null +++ b/frontend/src/Utilities/Array/sortByProp.ts @@ -0,0 +1,13 @@ +import { StringKey } from 'typings/Helpers/KeysMatching'; + +export function sortByProp< + // eslint-disable-next-line no-use-before-define + T extends Record, + K extends StringKey +>(sortKey: K) { + return (a: T, b: T) => { + return a[sortKey].localeCompare(b[sortKey], undefined, { numeric: true }); + }; +} + +export default sortByProp; diff --git a/frontend/src/Utilities/Number/formatBytes.js b/frontend/src/Utilities/Number/formatBytes.ts similarity index 75% rename from frontend/src/Utilities/Number/formatBytes.js rename to frontend/src/Utilities/Number/formatBytes.ts index d4d389357..a0ae8a985 100644 --- a/frontend/src/Utilities/Number/formatBytes.js +++ b/frontend/src/Utilities/Number/formatBytes.ts @@ -1,6 +1,6 @@ import { filesize } from 'filesize'; -function formatBytes(input) { +function formatBytes(input: string | number) { const size = Number(input); if (isNaN(size)) { @@ -9,7 +9,7 @@ function formatBytes(input) { return `${filesize(size, { base: 2, - round: 1 + round: 1, })}`; } diff --git a/frontend/src/Utilities/String/translate.ts b/frontend/src/Utilities/String/translate.ts index 1faa70ecc..72d3adf40 100644 --- a/frontend/src/Utilities/String/translate.ts +++ b/frontend/src/Utilities/String/translate.ts @@ -17,7 +17,7 @@ export async function fetchTranslations(): Promise { translations = data.Strings; resolve(true); - } catch (error) { + } catch { resolve(false); } }); diff --git a/frontend/src/Utilities/getPathWithUrlBase.js b/frontend/src/Utilities/getPathWithUrlBase.js deleted file mode 100644 index b687f2682..000000000 --- a/frontend/src/Utilities/getPathWithUrlBase.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function getPathWithUrlBase(path) { - return `${window.Prowlarr.urlBase}${path}`; -} diff --git a/frontend/src/Utilities/getPathWithUrlBase.ts b/frontend/src/Utilities/getPathWithUrlBase.ts new file mode 100644 index 000000000..948456728 --- /dev/null +++ b/frontend/src/Utilities/getPathWithUrlBase.ts @@ -0,0 +1,3 @@ +export default function getPathWithUrlBase(path: string) { + return `${window.Prowlarr.urlBase}${path}`; +} diff --git a/frontend/src/Utilities/getUniqueElementId.js b/frontend/src/Utilities/getUniqueElementId.js index dae5150b7..1b380851d 100644 --- a/frontend/src/Utilities/getUniqueElementId.js +++ b/frontend/src/Utilities/getUniqueElementId.js @@ -1,7 +1,9 @@ let i = 0; -// returns a HTML 4.0 compliant element IDs (http://stackoverflow.com/a/79022) - +/** + * @deprecated Use React's useId() instead + * @returns An HTML 4.0 compliant element IDs (http://stackoverflow.com/a/79022) + */ export default function getUniqueElementId() { return `id-${i++}`; } diff --git a/frontend/src/index.js b/frontend/src/index.js deleted file mode 100644 index acdfc6517..000000000 --- a/frontend/src/index.js +++ /dev/null @@ -1,26 +0,0 @@ -import { createBrowserHistory } from 'history'; -import React from 'react'; -import { render } from 'react-dom'; -import { fetchTranslations } from 'Utilities/String/translate'; - -import './preload'; -import './polyfills'; -import 'Styles/globals.css'; -import './index.css'; - -const history = createBrowserHistory(); -const hasTranslationsError = !await fetchTranslations(); - -const { default: createAppStore } = await import('Store/createAppStore'); -const { default: App } = await import('./App/App'); - -const store = createAppStore(history); - -render( - , - document.getElementById('root') -); diff --git a/frontend/src/typings/Health.ts b/frontend/src/typings/Health.ts new file mode 100644 index 000000000..66f385bbb --- /dev/null +++ b/frontend/src/typings/Health.ts @@ -0,0 +1,8 @@ +interface Health { + source: string; + type: string; + message: string; + wikiUrl: string; +} + +export default Health; diff --git a/frontend/src/typings/Helpers/KeysMatching.ts b/frontend/src/typings/Helpers/KeysMatching.ts new file mode 100644 index 000000000..0e20206ef --- /dev/null +++ b/frontend/src/typings/Helpers/KeysMatching.ts @@ -0,0 +1,7 @@ +type KeysMatching = { + [K in keyof T]-?: T[K] extends V ? K : never; +}[keyof T]; + +export type StringKey = KeysMatching; + +export default KeysMatching; diff --git a/frontend/src/typings/History.ts b/frontend/src/typings/History.ts index ff13c676a..3e50355dc 100644 --- a/frontend/src/typings/History.ts +++ b/frontend/src/typings/History.ts @@ -1,5 +1,12 @@ import ModelBase from 'App/ModelBase'; +export type HistoryQueryType = + | 'search' + | 'tvsearch' + | 'movie' + | 'book' + | 'music'; + export interface HistoryData { source: string; host: string; @@ -7,7 +14,7 @@ export interface HistoryData { offset: number; elapsedTime: number; query: string; - queryType: string; + queryType: HistoryQueryType; } interface History extends ModelBase { diff --git a/frontend/src/typings/IndexerStats.ts b/frontend/src/typings/IndexerStats.ts index 74fa1862e..ddbcebaec 100644 --- a/frontend/src/typings/IndexerStats.ts +++ b/frontend/src/typings/IndexerStats.ts @@ -2,6 +2,7 @@ export interface IndexerStatsIndexer { indexerId: number; indexerName: string; averageResponseTime: number; + averageGrabResponseTime: number; numberOfQueries: number; numberOfGrabs: number; numberOfRssQueries: number; diff --git a/frontend/src/typings/Settings/General.ts b/frontend/src/typings/Settings/General.ts new file mode 100644 index 000000000..c867bed74 --- /dev/null +++ b/frontend/src/typings/Settings/General.ts @@ -0,0 +1,45 @@ +export type UpdateMechanism = + | 'builtIn' + | 'script' + | 'external' + | 'apt' + | 'docker'; + +export default interface General { + bindAddress: string; + port: number; + sslPort: number; + enableSsl: boolean; + launchBrowser: boolean; + authenticationMethod: string; + authenticationRequired: string; + analyticsEnabled: boolean; + username: string; + password: string; + passwordConfirmation: string; + logLevel: string; + consoleLogLevel: string; + branch: string; + apiKey: string; + sslCertPath: string; + sslCertPassword: string; + urlBase: string; + instanceName: string; + applicationUrl: string; + updateAutomatically: boolean; + updateMechanism: UpdateMechanism; + updateScriptPath: string; + proxyEnabled: boolean; + proxyType: string; + proxyHostname: string; + proxyPort: number; + proxyUsername: string; + proxyPassword: string; + proxyBypassFilter: string; + proxyBypassLocalAddresses: boolean; + certificateValidation: string; + backupFolder: string; + backupInterval: number; + backupRetention: number; + id: number; +} diff --git a/frontend/src/typings/UiSettings.ts b/frontend/src/typings/Settings/UiSettings.ts similarity index 79% rename from frontend/src/typings/UiSettings.ts rename to frontend/src/typings/Settings/UiSettings.ts index 3c23a0356..656c4518b 100644 --- a/frontend/src/typings/UiSettings.ts +++ b/frontend/src/typings/Settings/UiSettings.ts @@ -1,4 +1,4 @@ -export interface UiSettings { +export default interface UiSettings { theme: 'auto' | 'dark' | 'light'; showRelativeDates: boolean; shortDateFormat: string; diff --git a/frontend/src/typings/SystemStatus.ts b/frontend/src/typings/SystemStatus.ts index e72be2c5c..d5eab3ca3 100644 --- a/frontend/src/typings/SystemStatus.ts +++ b/frontend/src/typings/SystemStatus.ts @@ -4,6 +4,8 @@ interface SystemStatus { authentication: string; branch: string; buildTime: string; + databaseVersion: string; + databaseType: string; instanceName: string; isAdmin: boolean; isDebug: boolean; @@ -18,7 +20,10 @@ interface SystemStatus { mode: string; osName: string; osVersion: string; + packageAuthor: string; packageUpdateMechanism: string; + packageUpdateMechanismMessage: string; + packageVersion: string; runtimeName: string; runtimeVersion: string; sqliteVersion: string; diff --git a/frontend/src/typings/Table.ts b/frontend/src/typings/Table.ts new file mode 100644 index 000000000..4f99e2045 --- /dev/null +++ b/frontend/src/typings/Table.ts @@ -0,0 +1,6 @@ +import Column from 'Components/Table/Column'; + +export interface TableOptionsChangePayload { + pageSize?: number; + columns: Column[]; +} diff --git a/frontend/src/typings/Task.ts b/frontend/src/typings/Task.ts new file mode 100644 index 000000000..57895d73e --- /dev/null +++ b/frontend/src/typings/Task.ts @@ -0,0 +1,13 @@ +import ModelBase from 'App/ModelBase'; + +interface Task extends ModelBase { + name: string; + taskName: string; + interval: number; + lastExecution: string; + lastStartTime: string; + nextExecution: string; + lastDuration: string; +} + +export default Task; diff --git a/frontend/src/typings/Update.ts b/frontend/src/typings/Update.ts new file mode 100644 index 000000000..448b1728d --- /dev/null +++ b/frontend/src/typings/Update.ts @@ -0,0 +1,20 @@ +export interface Changes { + new: string[]; + fixed: string[]; +} + +interface Update { + version: string; + branch: string; + releaseDate: string; + fileName: string; + url: string; + installed: boolean; + installedOn: string; + installable: boolean; + latest: boolean; + changes: Changes | null; + hash: string; +} + +export default Update; diff --git a/package.json b/package.json index bb99c1d7f..25960d641 100644 --- a/package.json +++ b/package.json @@ -23,35 +23,34 @@ "defaults" ], "dependencies": { - "@fortawesome/fontawesome-free": "6.4.0", - "@fortawesome/fontawesome-svg-core": "6.4.0", - "@fortawesome/free-regular-svg-icons": "6.4.0", - "@fortawesome/free-solid-svg-icons": "6.4.0", - "@fortawesome/react-fontawesome": "0.2.0", + "@fortawesome/fontawesome-free": "6.7.1", + "@fortawesome/fontawesome-svg-core": "6.7.1", + "@fortawesome/free-regular-svg-icons": "6.7.1", + "@fortawesome/free-solid-svg-icons": "6.7.1", + "@fortawesome/react-fontawesome": "0.2.2", "@juggle/resize-observer": "3.4.0", "@microsoft/signalr": "6.0.25", - "@sentry/browser": "7.100.0", - "@sentry/integrations": "7.100.0", - "@types/node": "18.19.31", + "@sentry/browser": "7.119.1", + "@sentry/integrations": "7.119.1", + "@types/node": "20.16.11", "@types/react": "18.2.79", "@types/react-dom": "18.2.25", - "chart.js": "4.4.2", - "classnames": "2.3.2", - "clipboard": "2.0.11", + "chart.js": "4.4.4", + "classnames": "2.5.1", "connected-react-router": "6.9.3", + "copy-to-clipboard": "3.3.3", "element-class": "0.2.2", - "filesize": "10.0.7", + "filesize": "10.1.6", "history": "4.10.1", - "https-browserify": "1.0.0", "jdu": "1.0.0", - "jquery": "3.7.0", + "jquery": "3.7.1", "lodash": "4.17.21", "mobile-detect": "1.4.5", - "moment": "2.29.4", + "moment": "2.30.1", "mousetrap": "1.6.5", "normalize.css": "8.0.1", "prop-types": "15.8.1", - "qs": "6.11.1", + "qs": "6.13.0", "react": "17.0.2", "react-addons-shallow-compare": "15.6.3", "react-async-script": "1.2.0", @@ -65,7 +64,6 @@ "react-dom": "17.0.2", "react-focus-lock": "2.9.4", "react-google-recaptcha": "2.1.0", - "react-lazyload": "3.2.0", "react-measure": "1.4.7", "react-popper": "1.3.7", "react-redux": "7.2.4", @@ -75,73 +73,72 @@ "react-text-truncate": "0.19.0", "react-use-measure": "2.1.1", "react-virtualized": "9.21.1", - "react-window": "1.8.8", + "react-window": "1.8.10", "redux": "4.2.1", "redux-actions": "2.6.5", "redux-batched-actions": "0.5.0", "redux-localstorage": "0.4.1", "redux-thunk": "2.4.2", - "reselect": "4.1.7", + "reselect": "4.1.8", "stacktrace-js": "2.0.2", - "typescript": "5.1.6" + "typescript": "5.7.2" }, "devDependencies": { - "@babel/core": "7.24.4", - "@babel/eslint-parser": "7.24.1", - "@babel/plugin-proposal-export-default-from": "7.24.1", + "@babel/core": "7.26.0", + "@babel/eslint-parser": "7.25.9", + "@babel/plugin-proposal-export-default-from": "7.25.9", "@babel/plugin-syntax-dynamic-import": "7.8.3", - "@babel/preset-env": "7.24.4", - "@babel/preset-react": "7.24.1", - "@babel/preset-typescript": "7.24.1", - "@types/lodash": "4.14.194", + "@babel/preset-env": "7.26.0", + "@babel/preset-react": "7.26.3", + "@babel/preset-typescript": "7.26.0", + "@types/lodash": "4.14.195", + "@types/react-document-title": "2.0.10", "@types/react-router-dom": "5.3.3", - "@types/react-text-truncate": "0.14.1", - "@types/react-window": "1.8.5", - "@types/webpack-livereload-plugin": "2.3.3", - "@typescript-eslint/eslint-plugin": "6.21.0", - "@typescript-eslint/parser": "6.21.0", + "@types/react-text-truncate": "0.19.0", + "@types/react-window": "1.8.8", + "@types/webpack-livereload-plugin": "2.3.6", + "@typescript-eslint/eslint-plugin": "8.18.1", + "@typescript-eslint/parser": "8.18.1", "are-you-es5": "2.1.2", - "autoprefixer": "10.4.14", - "babel-loader": "9.1.3", + "autoprefixer": "10.4.20", + "babel-loader": "9.2.1", "babel-plugin-inline-classnames": "2.0.1", "babel-plugin-transform-react-remove-prop-types": "0.4.24", - "core-js": "3.37.0", + "core-js": "3.39.0", "css-loader": "6.7.3", "css-modules-typescript-loader": "4.0.1", - "eslint": "8.57.0", + "eslint": "8.57.1", "eslint-config-prettier": "8.10.0", "eslint-plugin-filenames": "1.3.2", - "eslint-plugin-import": "2.29.1", + "eslint-plugin-import": "2.31.0", "eslint-plugin-prettier": "4.2.1", - "eslint-plugin-react": "7.34.1", - "eslint-plugin-react-hooks": "4.6.0", - "eslint-plugin-simple-import-sort": "12.1.0", + "eslint-plugin-react": "7.37.1", + "eslint-plugin-react-hooks": "4.6.2", + "eslint-plugin-simple-import-sort": "12.1.1", "file-loader": "6.2.0", "filemanager-webpack-plugin": "8.0.0", "fork-ts-checker-webpack-plugin": "8.0.0", - "html-webpack-plugin": "5.5.1", + "html-webpack-plugin": "5.6.0", "loader-utils": "^3.2.1", - "mini-css-extract-plugin": "2.7.5", - "postcss": "8.4.38", + "mini-css-extract-plugin": "2.9.1", + "postcss": "8.4.47", "postcss-color-function": "4.1.0", "postcss-loader": "7.3.0", "postcss-mixins": "9.0.4", - "postcss-nested": "6.0.1", + "postcss-nested": "6.2.0", "postcss-simple-vars": "7.0.1", "postcss-url": "10.1.3", "prettier": "2.8.8", "require-nocache": "1.0.0", - "rimraf": "4.4.1", - "run-sequence": "2.2.1", - "streamqueue": "1.1.2", + "rimraf": "6.0.1", "style-loader": "3.3.2", "stylelint": "15.6.1", - "stylelint-order": "6.0.3", - "terser-webpack-plugin": "5.3.9", - "ts-loader": "9.4.2", + "stylelint-order": "6.0.4", + "terser-webpack-plugin": "5.3.10", + "ts-loader": "9.5.1", "typescript-plugin-css-modules": "5.0.1", "url-loader": "4.1.1", - "webpack": "5.89.0", + "webpack": "5.95.0", "webpack-cli": "5.1.4", "webpack-livereload-plugin": "3.0.2" } diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 3608b0efa..ce3672c38 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -130,7 +130,7 @@ - + @@ -221,7 +221,7 @@ <_UsingDefaultRuntimeIdentifier>true - osx-x64 + osx-$(Architecture) diff --git a/src/NzbDrone.Automation.Test/Prowlarr.Automation.Test.csproj b/src/NzbDrone.Automation.Test/Prowlarr.Automation.Test.csproj index bb0b5fcc4..78c8b7d0f 100644 --- a/src/NzbDrone.Automation.Test/Prowlarr.Automation.Test.csproj +++ b/src/NzbDrone.Automation.Test/Prowlarr.Automation.Test.csproj @@ -4,7 +4,7 @@ - + diff --git a/src/NzbDrone.Common.Test/ExtensionTests/IPAddressExtensionsFixture.cs b/src/NzbDrone.Common.Test/ExtensionTests/IPAddressExtensionsFixture.cs index ff5d7383e..0f7ad3004 100644 --- a/src/NzbDrone.Common.Test/ExtensionTests/IPAddressExtensionsFixture.cs +++ b/src/NzbDrone.Common.Test/ExtensionTests/IPAddressExtensionsFixture.cs @@ -21,9 +21,28 @@ namespace NzbDrone.Common.Test.ExtensionTests [TestCase("1.2.3.4")] [TestCase("172.55.0.1")] [TestCase("192.55.0.1")] + [TestCase("100.64.0.1")] + [TestCase("100.127.255.254")] public void should_return_false_for_public_ip_address(string ipAddress) { IPAddress.Parse(ipAddress).IsLocalAddress().Should().BeFalse(); } + + [TestCase("100.64.0.1")] + [TestCase("100.127.255.254")] + [TestCase("100.100.100.100")] + public void should_return_true_for_cgnat_ip_address(string ipAddress) + { + IPAddress.Parse(ipAddress).IsCgnatIpAddress().Should().BeTrue(); + } + + [TestCase("1.2.3.4")] + [TestCase("192.168.5.1")] + [TestCase("100.63.255.255")] + [TestCase("100.128.0.0")] + public void should_return_false_for_non_cgnat_ip_address(string ipAddress) + { + IPAddress.Parse(ipAddress).IsCgnatIpAddress().Should().BeFalse(); + } } } diff --git a/src/NzbDrone.Common.Test/ExtensionTests/Int64ExtensionFixture.cs b/src/NzbDrone.Common.Test/ExtensionTests/NumberExtensionFixture.cs similarity index 91% rename from src/NzbDrone.Common.Test/ExtensionTests/Int64ExtensionFixture.cs rename to src/NzbDrone.Common.Test/ExtensionTests/NumberExtensionFixture.cs index 76e28f3f7..c51ab7ad4 100644 --- a/src/NzbDrone.Common.Test/ExtensionTests/Int64ExtensionFixture.cs +++ b/src/NzbDrone.Common.Test/ExtensionTests/NumberExtensionFixture.cs @@ -1,11 +1,11 @@ -using FluentAssertions; +using FluentAssertions; using NUnit.Framework; using NzbDrone.Common.Extensions; namespace NzbDrone.Common.Test.ExtensionTests { [TestFixture] - public class Int64ExtensionFixture + public class NumberExtensionFixture { [TestCase(0, "0 B")] [TestCase(1000, "1,000.0 B")] diff --git a/src/NzbDrone.Common.Test/InstrumentationTests/CleanseLogMessageFixture.cs b/src/NzbDrone.Common.Test/InstrumentationTests/CleanseLogMessageFixture.cs index 0c0a338e1..9e2b31d87 100644 --- a/src/NzbDrone.Common.Test/InstrumentationTests/CleanseLogMessageFixture.cs +++ b/src/NzbDrone.Common.Test/InstrumentationTests/CleanseLogMessageFixture.cs @@ -29,6 +29,8 @@ namespace NzbDrone.Common.Test.InstrumentationTests [TestCase(@"https://beyond-hd.me/torrent/download/the-next-365-days-2022-2160p-nf-web-dl-dual-ddp-51-dovi-hdr-hevc-apex.225146.2b51db35e1912ffc138825a12b9933d2")] [TestCase(@"https://anthelion.me/api.php?api_key=2b51db35e1910123321025a12b9933d2&o=json&t=movie&q=&tmdb=&imdb=&cat=&limit=100&offset=0")] [TestCase(@"https://avistaz.to/api/v1/jackett/auth: username=mySecret&password=mySecret&pid=mySecret")] + [TestCase(@"https://www.sharewood.tv/api/2b51db35e1910123321025a12b9933d2/last-torrents")] + [TestCase(@"https://example.org/rss/torrents?rsskey=2b51db35e1910123321025a12b9933d2&search=")] // Indexer and Download Client Responses diff --git a/src/NzbDrone.Common.Test/PathExtensionFixture.cs b/src/NzbDrone.Common.Test/PathExtensionFixture.cs index 344c6faf3..010bc3a02 100644 --- a/src/NzbDrone.Common.Test/PathExtensionFixture.cs +++ b/src/NzbDrone.Common.Test/PathExtensionFixture.cs @@ -133,11 +133,16 @@ namespace NzbDrone.Common.Test [TestCase(@"C:\test\", @"C:\Test\mydir")] [TestCase(@"C:\test", @"C:\Test\mydir\")] - public void path_should_be_parent_on_windows_only(string parentPath, string childPath) + public void windows_path_should_be_parent(string parentPath, string childPath) { - var expectedResult = OsInfo.IsWindows; + parentPath.IsParentPath(childPath).Should().Be(true); + } - parentPath.IsParentPath(childPath).Should().Be(expectedResult); + [TestCase("/test", "/test/mydir/")] + [TestCase("/test/", "/test/mydir")] + public void posix_path_should_be_parent(string parentPath, string childPath) + { + parentPath.IsParentPath(childPath).Should().Be(true); } [TestCase(@"C:\Test\mydir", @"C:\Test")] @@ -145,20 +150,57 @@ namespace NzbDrone.Common.Test [TestCase(@"C:\", null)] [TestCase(@"\\server\share", null)] [TestCase(@"\\server\share\test", @"\\server\share")] - public void path_should_return_parent_windows(string path, string parentPath) + public void windows_path_should_return_parent(string path, string parentPath) { - WindowsOnly(); path.GetParentPath().Should().Be(parentPath); } [TestCase(@"/", null)] [TestCase(@"/test", "/")] - public void path_should_return_parent_mono(string path, string parentPath) + [TestCase(@"/test/tv", "/test")] + public void unix_path_should_return_parent(string path, string parentPath) { - PosixOnly(); path.GetParentPath().Should().Be(parentPath); } + [TestCase(@"C:\Test\mydir", "Test")] + [TestCase(@"C:\Test\", @"C:\")] + [TestCase(@"C:\Test", @"C:\")] + [TestCase(@"C:\", null)] + [TestCase(@"\\server\share", null)] + [TestCase(@"\\server\share\test", @"\\server\share")] + public void path_should_return_parent_name_windows(string path, string parentPath) + { + path.GetParentName().Should().Be(parentPath); + } + + [TestCase(@"/", null)] + [TestCase(@"/test", "/")] + [TestCase(@"/test/tv", "test")] + public void path_should_return_parent_name_mono(string path, string parentPath) + { + path.GetParentName().Should().Be(parentPath); + } + + [TestCase(@"C:\Test\mydir", "mydir")] + [TestCase(@"C:\Test\", "Test")] + [TestCase(@"C:\Test", "Test")] + [TestCase(@"C:\", "C:\\")] + [TestCase(@"\\server\share", @"\\server\share")] + [TestCase(@"\\server\share\test", "test")] + public void path_should_return_directory_name_windows(string path, string parentPath) + { + path.GetDirectoryName().Should().Be(parentPath); + } + + [TestCase(@"/", "/")] + [TestCase(@"/test", "test")] + [TestCase(@"/test/tv", "tv")] + public void path_should_return_directory_name_mono(string path, string parentPath) + { + path.GetDirectoryName().Should().Be(parentPath); + } + [Test] public void path_should_return_parent_for_oversized_path() { @@ -166,7 +208,7 @@ namespace NzbDrone.Common.Test // This test will fail on Windows if long path support is not enabled: https://www.howtogeek.com/266621/how-to-make-windows-10-accept-file-paths-over-260-characters/ // It will also fail if the app isn't configured to use long path (such as resharper): https://blogs.msdn.microsoft.com/jeremykuhne/2016/07/30/net-4-6-2-and-long-paths-on-windows-10/ - var path = @"C:\media\2e168617-f2ae-43fb-b88c-3663af1c8eea\downloads\sabnzbd\nzbdrone\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories".AsOsAgnostic(); + var path = @"C:\media\2e168617-f2ae-43fb-b88c-3663af1c8eea\downloads\sabnzbd\nzbdrone\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories".AsOsAgnostic(); var parentPath = @"C:\media\2e168617-f2ae-43fb-b88c-3663af1c8eea\downloads\sabnzbd\nzbdrone\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing".AsOsAgnostic(); path.GetParentPath().Should().Be(parentPath); @@ -337,9 +379,59 @@ namespace NzbDrone.Common.Test [TestCase(@" C:\Test\TV\")] [TestCase(@" C:\Test\TV")] - public void IsPathValid_should_be_false(string path) + public void IsPathValid_should_be_false_on_windows(string path) { + WindowsOnly(); + path.IsPathValid(PathValidationType.CurrentOs).Should().BeFalse(); + } + + [TestCase(@"")] + [TestCase(@"relative/path")] + public void IsPathValid_should_be_false_on_unix(string path) + { + PosixOnly(); path.AsOsAgnostic().IsPathValid(PathValidationType.CurrentOs).Should().BeFalse(); } + + [TestCase(@"C:\", @"C:\")] + [TestCase(@"C:\\", @"C:\")] + [TestCase(@"C:\Test", @"C:\Test")] + [TestCase(@"C:\Test\", @"C:\Test")] + [TestCase(@"\\server\share", @"\\server\share")] + [TestCase(@"\\server\share\", @"\\server\share")] + public void windows_path_should_return_clean_path(string path, string cleanPath) + { + path.GetCleanPath().Should().Be(cleanPath); + } + + [TestCase("/", "/")] + [TestCase("//", "/")] + [TestCase("/test", "/test")] + [TestCase("/test/", "/test")] + [TestCase("/test//", "/test")] + public void unix_path_should_return_clean_path(string path, string cleanPath) + { + path.GetCleanPath().Should().Be(cleanPath); + } + + [TestCase(@"C:\Test\", @"C:\Test\Series Title", "Series Title")] + [TestCase(@"C:\Test\", @"C:\Test\Collection\Series Title", @"Collection\Series Title")] + [TestCase(@"C:\Test\mydir\", @"C:\Test\mydir\Collection\Series Title", @"Collection\Series Title")] + [TestCase(@"\\server\share", @"\\server\share\Series Title", "Series Title")] + [TestCase(@"\\server\share\mydir\", @"\\server\share\mydir\/Collection\Series Title", @"Collection\Series Title")] + public void windows_path_should_return_relative_path(string parentPath, string childPath, string relativePath) + { + parentPath.GetRelativePath(childPath).Should().Be(relativePath); + } + + [TestCase(@"/test", "/test/Series Title", "Series Title")] + [TestCase(@"/test/", "/test/Collection/Series Title", "Collection/Series Title")] + [TestCase(@"/test/mydir", "/test/mydir/Series Title", "Series Title")] + [TestCase(@"/test/mydir/", "/test/mydir/Collection/Series Title", "Collection/Series Title")] + [TestCase(@"/test/mydir/", @"/test/mydir/\Collection/Series Title", "Collection/Series Title")] + public void unix_path_should_return_relative_path(string parentPath, string childPath, string relativePath) + { + parentPath.GetRelativePath(childPath).Should().Be(relativePath); + } } } diff --git a/src/NzbDrone.Common/ArchiveService.cs b/src/NzbDrone.Common/ArchiveService.cs index 800d240ab..d420bbbc0 100644 --- a/src/NzbDrone.Common/ArchiveService.cs +++ b/src/NzbDrone.Common/ArchiveService.cs @@ -42,17 +42,18 @@ namespace NzbDrone.Common public void CreateZip(string path, IEnumerable files) { - using (var zipFile = ZipFile.Create(path)) + _logger.Debug("Creating archive {0}", path); + + using var zipFile = ZipFile.Create(path); + + zipFile.BeginUpdate(); + + foreach (var file in files) { - zipFile.BeginUpdate(); - - foreach (var file in files) - { - zipFile.Add(file, Path.GetFileName(file)); - } - - zipFile.CommitUpdate(); + zipFile.Add(file, Path.GetFileName(file)); } + + zipFile.CommitUpdate(); } private void ExtractZip(string compressedFile, string destination) diff --git a/src/NzbDrone.Common/Disk/DiskProviderBase.cs b/src/NzbDrone.Common/Disk/DiskProviderBase.cs index d92c01d00..621d4b258 100644 --- a/src/NzbDrone.Common/Disk/DiskProviderBase.cs +++ b/src/NzbDrone.Common/Disk/DiskProviderBase.cs @@ -153,7 +153,11 @@ namespace NzbDrone.Common.Disk { Ensure.That(path, () => path).IsValidPath(PathValidationType.CurrentOs); - return Directory.EnumerateDirectories(path); + return Directory.EnumerateDirectories(path, "*", new EnumerationOptions + { + AttributesToSkip = FileAttributes.System, + IgnoreInaccessible = true + }); } public IEnumerable GetFiles(string path, bool recursive) @@ -185,6 +189,25 @@ namespace NzbDrone.Common.Disk } var fi = new FileInfo(path); + + try + { + // If the file is a symlink, resolve the target path and get the size of the target file. + if (fi.Attributes.HasFlag(FileAttributes.ReparsePoint)) + { + var targetPath = fi.ResolveLinkTarget(true)?.FullName; + + if (targetPath != null) + { + fi = new FileInfo(targetPath); + } + } + } + catch (IOException ex) + { + Logger.Trace(ex, "Unable to resolve symlink target for {0}", path); + } + return fi.Length; } diff --git a/src/NzbDrone.Common/Disk/OsPath.cs b/src/NzbDrone.Common/Disk/OsPath.cs index f6f01fccf..30661d747 100644 --- a/src/NzbDrone.Common/Disk/OsPath.cs +++ b/src/NzbDrone.Common/Disk/OsPath.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Text.RegularExpressions; using NzbDrone.Common.Extensions; namespace NzbDrone.Common.Disk @@ -9,6 +10,8 @@ namespace NzbDrone.Common.Disk private readonly string _path; private readonly OsPathKind _kind; + private static readonly Regex UncPathRegex = new Regex(@"(?^\\\\(?:\?\\UNC\\)?[^\\]+\\[^\\]+)(?:\\|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase); + public OsPath(string path) { if (path == null) @@ -96,6 +99,29 @@ namespace NzbDrone.Common.Disk return path; } + private static string TrimTrailingSlash(string path, OsPathKind kind) + { + switch (kind) + { + case OsPathKind.Windows when !path.EndsWith(":\\"): + while (!path.EndsWith(":\\") && path.EndsWith('\\')) + { + path = path[..^1]; + } + + return path; + case OsPathKind.Unix when path != "/": + while (path != "/" && path.EndsWith('/')) + { + path = path[..^1]; + } + + return path; + } + + return path; + } + public OsPathKind Kind => _kind; public bool IsWindowsPath => _kind == OsPathKind.Windows; @@ -130,7 +156,19 @@ namespace NzbDrone.Common.Disk if (index == -1) { - return new OsPath(null); + return Null; + } + + var rootLength = GetRootLength(); + + if (rootLength == _path.Length) + { + return Null; + } + + if (rootLength > index + 1) + { + return new OsPath(_path.Substring(0, rootLength)); } return new OsPath(_path.Substring(0, index), _kind).AsDirectory(); @@ -139,6 +177,8 @@ namespace NzbDrone.Common.Disk public string FullPath => _path; + public string PathWithoutTrailingSlash => TrimTrailingSlash(_path, _kind); + public string FileName { get @@ -161,6 +201,29 @@ namespace NzbDrone.Common.Disk } } + public string Name + { + // Meant to behave similar to DirectoryInfo.Name + get + { + var index = GetFileNameIndex(); + + if (index == -1) + { + return PathWithoutTrailingSlash; + } + + var rootLength = GetRootLength(); + + if (rootLength > index + 1) + { + return _path.Substring(0, rootLength); + } + + return TrimTrailingSlash(_path.Substring(index).TrimStart('/', '\\'), _kind); + } + } + public bool IsValid => _path.IsPathValid(PathValidationType.CurrentOs); private int GetFileNameIndex() @@ -190,11 +253,50 @@ namespace NzbDrone.Common.Disk return index; } + private int GetRootLength() + { + if (!IsRooted) + { + return 0; + } + + if (_kind == OsPathKind.Unix) + { + return 1; + } + + if (_kind == OsPathKind.Windows) + { + if (HasWindowsDriveLetter(_path)) + { + return 3; + } + + var uncMatch = UncPathRegex.Match(_path); + + // \\?\UNC\server\share\ or \\server\share + if (uncMatch.Success) + { + return uncMatch.Groups["unc"].Length; + } + + // \\?\C:\ + if (_path.StartsWith(@"\\?\")) + { + return 7; + } + } + + return 0; + } + private string[] GetFragments() { return _path.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries); } + public static OsPath Null => new (null); + public override string ToString() { return _path; @@ -267,6 +369,11 @@ namespace NzbDrone.Common.Disk } public bool Equals(OsPath other) + { + return Equals(other, false); + } + + public bool Equals(OsPath other, bool ignoreTrailingSlash) { if (ReferenceEquals(other, null)) { @@ -278,8 +385,8 @@ namespace NzbDrone.Common.Disk return true; } - var left = _path; - var right = other._path; + var left = ignoreTrailingSlash ? PathWithoutTrailingSlash : _path; + var right = ignoreTrailingSlash ? other.PathWithoutTrailingSlash : other._path; if (Kind == OsPathKind.Windows || other.Kind == OsPathKind.Windows) { diff --git a/src/NzbDrone.Common/EnvironmentInfo/OsInfo.cs b/src/NzbDrone.Common/EnvironmentInfo/OsInfo.cs index b9a206e4e..aece27859 100644 --- a/src/NzbDrone.Common/EnvironmentInfo/OsInfo.cs +++ b/src/NzbDrone.Common/EnvironmentInfo/OsInfo.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using NLog; @@ -25,22 +24,25 @@ namespace NzbDrone.Common.EnvironmentInfo static OsInfo() { - var platform = Environment.OSVersion.Platform; - - switch (platform) + if (OperatingSystem.IsWindows()) { - case PlatformID.Win32NT: - { - Os = Os.Windows; - break; - } - - case PlatformID.MacOSX: - case PlatformID.Unix: - { - Os = GetPosixFlavour(); - break; - } + Os = Os.Windows; + } + else if (OperatingSystem.IsMacOS()) + { + Os = Os.Osx; + } + else if (OperatingSystem.IsFreeBSD()) + { + Os = Os.Bsd; + } + else + { +#if ISMUSL + Os = Os.LinuxMusl; +#else + Os = Os.Linux; +#endif } } @@ -84,59 +86,6 @@ namespace NzbDrone.Common.EnvironmentInfo IsDocker = true; } } - - private static Os GetPosixFlavour() - { - var output = RunAndCapture("uname", "-s"); - - if (output.StartsWith("Darwin")) - { - return Os.Osx; - } - else if (output.Contains("BSD")) - { - return Os.Bsd; - } - else - { -#if ISMUSL - return Os.LinuxMusl; -#else - return Os.Linux; -#endif - } - } - - private static string RunAndCapture(string filename, string args) - { - var processStartInfo = new ProcessStartInfo - { - FileName = filename, - Arguments = args, - UseShellExecute = false, - CreateNoWindow = true, - RedirectStandardOutput = true - }; - - var output = string.Empty; - - try - { - using (var p = Process.Start(processStartInfo)) - { - // To avoid deadlocks, always read the output stream first and then wait. - output = p.StandardOutput.ReadToEnd(); - - p.WaitForExit(1000); - } - } - catch (Exception) - { - output = string.Empty; - } - - return output; - } } public interface IOsInfo diff --git a/src/NzbDrone.Common/Extensions/IpAddressExtensions.cs b/src/NzbDrone.Common/Extensions/IpAddressExtensions.cs index 7feb431c4..cbc1f5f83 100644 --- a/src/NzbDrone.Common/Extensions/IpAddressExtensions.cs +++ b/src/NzbDrone.Common/Extensions/IpAddressExtensions.cs @@ -39,18 +39,24 @@ namespace NzbDrone.Common.Extensions private static bool IsLocalIPv4(byte[] ipv4Bytes) { // Link local (no IP assigned by DHCP): 169.254.0.0 to 169.254.255.255 (169.254.0.0/16) - bool IsLinkLocal() => ipv4Bytes[0] == 169 && ipv4Bytes[1] == 254; + var isLinkLocal = ipv4Bytes[0] == 169 && ipv4Bytes[1] == 254; // Class A private range: 10.0.0.0 – 10.255.255.255 (10.0.0.0/8) - bool IsClassA() => ipv4Bytes[0] == 10; + var isClassA = ipv4Bytes[0] == 10; // Class B private range: 172.16.0.0 – 172.31.255.255 (172.16.0.0/12) - bool IsClassB() => ipv4Bytes[0] == 172 && ipv4Bytes[1] >= 16 && ipv4Bytes[1] <= 31; + var isClassB = ipv4Bytes[0] == 172 && ipv4Bytes[1] >= 16 && ipv4Bytes[1] <= 31; // Class C private range: 192.168.0.0 – 192.168.255.255 (192.168.0.0/16) - bool IsClassC() => ipv4Bytes[0] == 192 && ipv4Bytes[1] == 168; + var isClassC = ipv4Bytes[0] == 192 && ipv4Bytes[1] == 168; - return IsLinkLocal() || IsClassA() || IsClassC() || IsClassB(); + return isLinkLocal || isClassA || isClassC || isClassB; + } + + public static bool IsCgnatIpAddress(this IPAddress ipAddress) + { + var bytes = ipAddress.GetAddressBytes(); + return bytes.Length == 4 && bytes[0] == 100 && bytes[1] >= 64 && bytes[1] <= 127; } } } diff --git a/src/NzbDrone.Common/Extensions/Int64Extensions.cs b/src/NzbDrone.Common/Extensions/NumberExtensions.cs similarity index 53% rename from src/NzbDrone.Common/Extensions/Int64Extensions.cs rename to src/NzbDrone.Common/Extensions/NumberExtensions.cs index bfca7f66c..15037b20b 100644 --- a/src/NzbDrone.Common/Extensions/Int64Extensions.cs +++ b/src/NzbDrone.Common/Extensions/NumberExtensions.cs @@ -1,9 +1,9 @@ -using System; +using System; using System.Globalization; namespace NzbDrone.Common.Extensions { - public static class Int64Extensions + public static class NumberExtensions { private static readonly string[] SizeSuffixes = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; @@ -26,5 +26,25 @@ namespace NzbDrone.Common.Extensions return string.Format(CultureInfo.InvariantCulture, "{0:n1} {1}", adjustedSize, SizeSuffixes[mag]); } + + public static long Megabytes(this int megabytes) + { + return Convert.ToInt64(megabytes * 1024L * 1024L); + } + + public static long Gigabytes(this int gigabytes) + { + return Convert.ToInt64(gigabytes * 1024L * 1024L * 1024L); + } + + public static long Megabytes(this double megabytes) + { + return Convert.ToInt64(megabytes * 1024L * 1024L); + } + + public static long Gigabytes(this double gigabytes) + { + return Convert.ToInt64(gigabytes * 1024L * 1024L * 1024L); + } } } diff --git a/src/NzbDrone.Common/Extensions/PathExtensions.cs b/src/NzbDrone.Common/Extensions/PathExtensions.cs index 4816c9f27..30a467f21 100644 --- a/src/NzbDrone.Common/Extensions/PathExtensions.cs +++ b/src/NzbDrone.Common/Extensions/PathExtensions.cs @@ -25,8 +25,6 @@ namespace NzbDrone.Common.Extensions private static readonly string UPDATE_CLIENT_FOLDER_NAME = "Prowlarr.Update" + Path.DirectorySeparatorChar; private static readonly string UPDATE_LOG_FOLDER_NAME = "UpdateLogs" + Path.DirectorySeparatorChar; - private static readonly Regex PARENT_PATH_END_SLASH_REGEX = new Regex(@"(?[^&=""]+?)(?=[ ""&=]|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new (@"(?<=[?&: ;])(apikey|api_key|(?:(?:access|api)[-_]?)?token|pass(?:key|wd)?|auth|authkey|rsskey|user|u?id|api|[a-z_]*apikey|account|pid|pwd)=(?[^&=""]+?)(?=[ ""&=]|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"(?<=[?& ;])[^=]*?(_?(?[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"rss(24h)?\.torrentleech\.org/(?!rss)(?[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"torrentleech\.org/rss/download/[0-9]+/(?[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), @@ -21,6 +21,7 @@ namespace NzbDrone.Common.Instrumentation new (@"(?<=authkey = "")(?[^&=]+?)(?="")", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"(?<=beyond-hd\.[a-z]+/api/torrents/)(?[^&=][a-z0-9]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"(?<=beyond-hd\.[a-z]+/torrent/download/[\w\d-]+[.]\d+[.])(?[a-z0-9]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new (@"(?:sharewood)\.[a-z]{2,3}/api/(?[a-z0-9]{16,})/", RegexOptions.Compiled | RegexOptions.IgnoreCase), // UNIT3D new (@"(?<=[a-z0-9-]+\.[a-z]+/torrent/download/\d+\.)(?[^&=][a-z0-9]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), diff --git a/src/NzbDrone.Common/Instrumentation/CleansingClefLogLayout.cs b/src/NzbDrone.Common/Instrumentation/CleansingClefLogLayout.cs new file mode 100644 index 000000000..f110b96ac --- /dev/null +++ b/src/NzbDrone.Common/Instrumentation/CleansingClefLogLayout.cs @@ -0,0 +1,21 @@ +using System.Text; +using NLog; +using NLog.Layouts.ClefJsonLayout; +using NzbDrone.Common.EnvironmentInfo; + +namespace NzbDrone.Common.Instrumentation; + +public class CleansingClefLogLayout : CompactJsonLayout +{ + protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target) + { + base.RenderFormattedMessage(logEvent, target); + + if (RuntimeInfo.IsProduction) + { + var result = CleanseLogMessage.Cleanse(target.ToString()); + target.Clear(); + target.Append(result); + } + } +} diff --git a/src/NzbDrone.Common/Instrumentation/CleansingConsoleLogLayout.cs b/src/NzbDrone.Common/Instrumentation/CleansingConsoleLogLayout.cs new file mode 100644 index 000000000..f894a4df5 --- /dev/null +++ b/src/NzbDrone.Common/Instrumentation/CleansingConsoleLogLayout.cs @@ -0,0 +1,26 @@ +using System.Text; +using NLog; +using NLog.Layouts; +using NzbDrone.Common.EnvironmentInfo; + +namespace NzbDrone.Common.Instrumentation; + +public class CleansingConsoleLogLayout : SimpleLayout +{ + public CleansingConsoleLogLayout(string format) + : base(format) + { + } + + protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target) + { + base.RenderFormattedMessage(logEvent, target); + + if (RuntimeInfo.IsProduction) + { + var result = CleanseLogMessage.Cleanse(target.ToString()); + target.Clear(); + target.Append(result); + } + } +} diff --git a/src/NzbDrone.Common/Instrumentation/NzbDroneFileTarget.cs b/src/NzbDrone.Common/Instrumentation/CleansingFileTarget.cs similarity index 87% rename from src/NzbDrone.Common/Instrumentation/NzbDroneFileTarget.cs rename to src/NzbDrone.Common/Instrumentation/CleansingFileTarget.cs index 84658cf74..f74d1fca4 100644 --- a/src/NzbDrone.Common/Instrumentation/NzbDroneFileTarget.cs +++ b/src/NzbDrone.Common/Instrumentation/CleansingFileTarget.cs @@ -4,7 +4,7 @@ using NLog.Targets; namespace NzbDrone.Common.Instrumentation { - public class NzbDroneFileTarget : FileTarget + public class CleansingFileTarget : FileTarget { protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target) { diff --git a/src/NzbDrone.Common/Instrumentation/NzbDroneLogger.cs b/src/NzbDrone.Common/Instrumentation/NzbDroneLogger.cs index f390d8028..d9fdd5b25 100644 --- a/src/NzbDrone.Common/Instrumentation/NzbDroneLogger.cs +++ b/src/NzbDrone.Common/Instrumentation/NzbDroneLogger.cs @@ -12,7 +12,11 @@ namespace NzbDrone.Common.Instrumentation { public static class NzbDroneLogger { - private const string FILE_LOG_LAYOUT = @"${date:format=yyyy-MM-dd HH\:mm\:ss.f}|${level}|${logger}|${message}${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}"; + private const string FileLogLayout = @"${date:format=yyyy-MM-dd HH\:mm\:ss.f}|${level}|${logger}|${message}${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}"; + private const string ConsoleFormat = "[${level}] ${logger}: ${message} ${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}"; + + private static readonly CleansingConsoleLogLayout CleansingConsoleLayout = new (ConsoleFormat); + private static readonly CleansingClefLogLayout ClefLogLayout = new (); private static bool _isConfigured; @@ -104,17 +108,6 @@ namespace NzbDrone.Common.Instrumentation LogManager.Configuration.LoggingRules.Add(loggingRule); } - private static void RegisterGlobalFilters() - { - LogManager.Setup().LoadConfiguration(c => - { - c.ForLogger("System.*").WriteToNil(LogLevel.Warn); - c.ForLogger("Microsoft.*").WriteToNil(LogLevel.Warn); - c.ForLogger("Microsoft.Hosting.Lifetime*").WriteToNil(LogLevel.Info); - c.ForLogger("Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware").WriteToNil(LogLevel.Fatal); - }); - } - private static void RegisterConsole() { var level = LogLevel.Trace; @@ -122,7 +115,12 @@ namespace NzbDrone.Common.Instrumentation var coloredConsoleTarget = new ColoredConsoleTarget(); coloredConsoleTarget.Name = "consoleLogger"; - coloredConsoleTarget.Layout = "[${level}] ${logger}: ${message} ${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}"; + + var logFormat = Enum.TryParse(Environment.GetEnvironmentVariable("PROWLARR__LOG__CONSOLEFORMAT"), out var formatEnumValue) + ? formatEnumValue + : ConsoleLogFormat.Standard; + + ConfigureConsoleLayout(coloredConsoleTarget, logFormat); var loggingRule = new LoggingRule("*", level, coloredConsoleTarget); @@ -139,7 +137,7 @@ namespace NzbDrone.Common.Instrumentation private static void RegisterAppFile(IAppFolderInfo appFolderInfo, string name, string fileName, int maxArchiveFiles, LogLevel minLogLevel) { - var fileTarget = new NzbDroneFileTarget(); + var fileTarget = new CleansingFileTarget(); fileTarget.Name = name; fileTarget.FileName = Path.Combine(appFolderInfo.GetLogFolder(), fileName); @@ -148,11 +146,11 @@ namespace NzbDrone.Common.Instrumentation fileTarget.ConcurrentWrites = false; fileTarget.ConcurrentWriteAttemptDelay = 50; fileTarget.ConcurrentWriteAttempts = 10; - fileTarget.ArchiveAboveSize = 1024000; + fileTarget.ArchiveAboveSize = 1.Megabytes(); fileTarget.MaxArchiveFiles = maxArchiveFiles; fileTarget.EnableFileDelete = true; fileTarget.ArchiveNumbering = ArchiveNumberingMode.Rolling; - fileTarget.Layout = FILE_LOG_LAYOUT; + fileTarget.Layout = FileLogLayout; var loggingRule = new LoggingRule("*", minLogLevel, fileTarget); @@ -171,7 +169,7 @@ namespace NzbDrone.Common.Instrumentation fileTarget.ConcurrentWrites = false; fileTarget.ConcurrentWriteAttemptDelay = 50; fileTarget.ConcurrentWriteAttempts = 100; - fileTarget.Layout = FILE_LOG_LAYOUT; + fileTarget.Layout = FileLogLayout; var loggingRule = new LoggingRule("*", LogLevel.Trace, fileTarget); @@ -196,6 +194,17 @@ namespace NzbDrone.Common.Instrumentation LogManager.Configuration.LoggingRules.Insert(0, rule); } + private static void RegisterGlobalFilters() + { + LogManager.Setup().LoadConfiguration(c => + { + c.ForLogger("System.*").WriteToNil(LogLevel.Warn); + c.ForLogger("Microsoft.*").WriteToNil(LogLevel.Warn); + c.ForLogger("Microsoft.Hosting.Lifetime*").WriteToNil(LogLevel.Info); + c.ForLogger("Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware").WriteToNil(LogLevel.Fatal); + }); + } + public static Logger GetLogger(Type obj) { return LogManager.GetLogger(obj.Name.Replace("NzbDrone.", "")); @@ -205,5 +214,20 @@ namespace NzbDrone.Common.Instrumentation { return GetLogger(obj.GetType()); } + + public static void ConfigureConsoleLayout(ColoredConsoleTarget target, ConsoleLogFormat format) + { + target.Layout = format switch + { + ConsoleLogFormat.Clef => NzbDroneLogger.ClefLogLayout, + _ => NzbDroneLogger.CleansingConsoleLayout + }; + } + } + + public enum ConsoleLogFormat + { + Standard, + Clef } } diff --git a/src/NzbDrone.Common/Instrumentation/Sentry/SentryTarget.cs b/src/NzbDrone.Common/Instrumentation/Sentry/SentryTarget.cs index 843f904a6..3a4737f74 100644 --- a/src/NzbDrone.Common/Instrumentation/Sentry/SentryTarget.cs +++ b/src/NzbDrone.Common/Instrumentation/Sentry/SentryTarget.cs @@ -119,7 +119,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry o.Environment = BuildInfo.Branch; // Crash free run statistics (sends a ping for healthy and for crashes sessions) - o.AutoSessionTracking = true; + o.AutoSessionTracking = false; // Caches files in the event device is offline // Sentry creates a 'sentry' sub directory, no need to concat here @@ -148,7 +148,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry _debounce = new SentryDebounce(); // initialize to true and reconfigure later - // Otherwise it will default to false and any errors occuring + // Otherwise it will default to false and any errors occurring // before config file gets read will not be filtered FilterEvents = true; SentryEnabled = true; @@ -207,9 +207,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry private void OnError(Exception ex) { - var webException = ex as WebException; - - if (webException != null) + if (ex is WebException webException) { var response = webException.Response as HttpWebResponse; var statusCode = response?.StatusCode; diff --git a/src/NzbDrone.Common/Options/AuthOptions.cs b/src/NzbDrone.Common/Options/AuthOptions.cs index 2b63308d3..64330b68b 100644 --- a/src/NzbDrone.Common/Options/AuthOptions.cs +++ b/src/NzbDrone.Common/Options/AuthOptions.cs @@ -6,4 +6,5 @@ public class AuthOptions public bool? Enabled { get; set; } public string Method { get; set; } public string Required { get; set; } + public bool? TrustCgnatIpAddresses { get; set; } } diff --git a/src/NzbDrone.Common/Options/LogOptions.cs b/src/NzbDrone.Common/Options/LogOptions.cs index 1529bb1d0..6460eeaa6 100644 --- a/src/NzbDrone.Common/Options/LogOptions.cs +++ b/src/NzbDrone.Common/Options/LogOptions.cs @@ -5,8 +5,10 @@ public class LogOptions public string Level { get; set; } public bool? FilterSentryEvents { get; set; } public int? Rotate { get; set; } + public int? SizeLimit { get; set; } public bool? Sql { get; set; } public string ConsoleLevel { get; set; } + public string ConsoleFormat { get; set; } public bool? AnalyticsEnabled { get; set; } public string SyslogServer { get; set; } public int? SyslogPort { get; set; } diff --git a/src/NzbDrone.Common/Processes/ProcessProvider.cs b/src/NzbDrone.Common/Processes/ProcessProvider.cs index 5db0565e0..c68207a09 100644 --- a/src/NzbDrone.Common/Processes/ProcessProvider.cs +++ b/src/NzbDrone.Common/Processes/ProcessProvider.cs @@ -6,6 +6,7 @@ using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; +using System.Text; using NLog; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Model; @@ -117,7 +118,9 @@ namespace NzbDrone.Common.Processes UseShellExecute = false, RedirectStandardError = true, RedirectStandardOutput = true, - RedirectStandardInput = true + RedirectStandardInput = true, + StandardOutputEncoding = Encoding.UTF8, + StandardErrorEncoding = Encoding.UTF8 }; if (environmentVariables != null) @@ -313,7 +316,7 @@ namespace NzbDrone.Common.Processes processInfo = new ProcessInfo(); processInfo.Id = process.Id; processInfo.Name = process.ProcessName; - processInfo.StartPath = process.MainModule.FileName; + processInfo.StartPath = process.MainModule?.FileName; if (process.Id != GetCurrentProcessId() && process.HasExited) { diff --git a/src/NzbDrone.Common/Prowlarr.Common.csproj b/src/NzbDrone.Common/Prowlarr.Common.csproj index d90f7738e..106890399 100644 --- a/src/NzbDrone.Common/Prowlarr.Common.csproj +++ b/src/NzbDrone.Common/Prowlarr.Common.csproj @@ -5,18 +5,21 @@ - - + + + - - - + + + + - + + - + diff --git a/src/NzbDrone.Core.Test/Datastore/Converters/TimeSpanConverterFixture.cs b/src/NzbDrone.Core.Test/Datastore/Converters/TimeSpanConverterFixture.cs new file mode 100644 index 000000000..79d0adaee --- /dev/null +++ b/src/NzbDrone.Core.Test/Datastore/Converters/TimeSpanConverterFixture.cs @@ -0,0 +1,43 @@ +using System; +using System.Data.SQLite; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Datastore.Converters; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.Datastore.Converters; + +[TestFixture] +public class TimeSpanConverterFixture : CoreTest +{ + private SQLiteParameter _param; + + [SetUp] + public void Setup() + { + _param = new SQLiteParameter(); + } + + [Test] + public void should_return_string_when_saving_timespan_to_db() + { + var span = TimeSpan.FromMilliseconds(10); + + Subject.SetValue(_param, span); + _param.Value.Should().Be(span.ToString()); + } + + [Test] + public void should_return_timespan_when_getting_string_from_db() + { + var span = TimeSpan.FromMilliseconds(10); + + Subject.Parse(span.ToString()).Should().Be(span); + } + + [Test] + public void should_return_zero_timespan_for_db_null_value_when_getting_from_db() + { + Subject.Parse(null).Should().Be(TimeSpan.Zero); + } +} diff --git a/src/NzbDrone.Core.Test/Datastore/DatabaseVersionParserFixture.cs b/src/NzbDrone.Core.Test/Datastore/DatabaseVersionParserFixture.cs new file mode 100644 index 000000000..05bf04fea --- /dev/null +++ b/src/NzbDrone.Core.Test/Datastore/DatabaseVersionParserFixture.cs @@ -0,0 +1,38 @@ +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Datastore; + +namespace NzbDrone.Core.Test.Datastore; + +[TestFixture] +public class DatabaseVersionParserFixture +{ + [TestCase("3.44.2", 3, 44, 2)] + public void should_parse_sqlite_database_version(string serverVersion, int majorVersion, int minorVersion, int buildVersion) + { + var version = DatabaseVersionParser.ParseServerVersion(serverVersion); + + version.Should().NotBeNull(); + version.Major.Should().Be(majorVersion); + version.Minor.Should().Be(minorVersion); + version.Build.Should().Be(buildVersion); + } + + [TestCase("14.8 (Debian 14.8-1.pgdg110+1)", 14, 8, null)] + [TestCase("16.3 (Debian 16.3-1.pgdg110+1)", 16, 3, null)] + [TestCase("16.3 - Percona Distribution", 16, 3, null)] + [TestCase("17.0 - Percona Server", 17, 0, null)] + public void should_parse_postgres_database_version(string serverVersion, int majorVersion, int minorVersion, int? buildVersion) + { + var version = DatabaseVersionParser.ParseServerVersion(serverVersion); + + version.Should().NotBeNull(); + version.Major.Should().Be(majorVersion); + version.Minor.Should().Be(minorVersion); + + if (buildVersion.HasValue) + { + version.Build.Should().Be(buildVersion.Value); + } + } +} diff --git a/src/NzbDrone.Core.Test/HealthCheck/Checks/UpdateCheckFixture.cs b/src/NzbDrone.Core.Test/HealthCheck/Checks/UpdateCheckFixture.cs index 64eeb9169..7d859eb9d 100644 --- a/src/NzbDrone.Core.Test/HealthCheck/Checks/UpdateCheckFixture.cs +++ b/src/NzbDrone.Core.Test/HealthCheck/Checks/UpdateCheckFixture.cs @@ -7,6 +7,7 @@ using NzbDrone.Core.HealthCheck.Checks; using NzbDrone.Core.Localization; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Update; +using NzbDrone.Test.Common; namespace NzbDrone.Core.Test.HealthCheck.Checks { @@ -21,28 +22,10 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks .Returns("Some Warning Message"); } - [Test] - public void should_return_error_when_app_folder_is_write_protected() - { - WindowsOnly(); - - Mocker.GetMock() - .Setup(s => s.StartUpFolder) - .Returns(@"C:\NzbDrone"); - - Mocker.GetMock() - .Setup(c => c.FolderWritable(It.IsAny())) - .Returns(false); - - Subject.Check().ShouldBeError(); - } - [Test] public void should_return_error_when_app_folder_is_write_protected_and_update_automatically_is_enabled() { - PosixOnly(); - - const string startupFolder = @"/opt/nzbdrone"; + var startupFolder = @"C:\NzbDrone".AsOsAgnostic(); Mocker.GetMock() .Setup(s => s.UpdateAutomatically) @@ -62,10 +45,8 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks [Test] public void should_return_error_when_ui_folder_is_write_protected_and_update_automatically_is_enabled() { - PosixOnly(); - - const string startupFolder = @"/opt/nzbdrone"; - const string uiFolder = @"/opt/nzbdrone/UI"; + var startupFolder = @"C:\NzbDrone".AsOsAgnostic(); + var uiFolder = @"C:\NzbDrone\UI".AsOsAgnostic(); Mocker.GetMock() .Setup(s => s.UpdateAutomatically) @@ -89,7 +70,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks [Test] public void should_not_return_error_when_app_folder_is_write_protected_and_external_script_enabled() { - PosixOnly(); + var startupFolder = @"C:\NzbDrone".AsOsAgnostic(); Mocker.GetMock() .Setup(s => s.UpdateAutomatically) @@ -101,7 +82,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks Mocker.GetMock() .Setup(s => s.StartUpFolder) - .Returns(@"/opt/nzbdrone"); + .Returns(startupFolder); Mocker.GetMock() .Verify(c => c.FolderWritable(It.IsAny()), Times.Never()); diff --git a/src/NzbDrone.Core.Test/Http/HttpProxySettingsProviderFixture.cs b/src/NzbDrone.Core.Test/Http/HttpProxySettingsProviderFixture.cs index 067149904..2beeb16f9 100644 --- a/src/NzbDrone.Core.Test/Http/HttpProxySettingsProviderFixture.cs +++ b/src/NzbDrone.Core.Test/Http/HttpProxySettingsProviderFixture.cs @@ -12,7 +12,7 @@ namespace NzbDrone.Core.Test.Http { private HttpProxySettings GetProxySettings() { - return new HttpProxySettings(ProxyType.Socks5, "localhost", 8080, "*.httpbin.org,google.com", true, null, null); + return new HttpProxySettings(ProxyType.Socks5, "localhost", 8080, "*.httpbin.org,google.com,172.16.0.0/12", true, null, null); } [Test] @@ -23,6 +23,7 @@ namespace NzbDrone.Core.Test.Http Subject.ShouldProxyBeBypassed(settings, new HttpUri("http://eu.httpbin.org/get")).Should().BeTrue(); Subject.ShouldProxyBeBypassed(settings, new HttpUri("http://google.com/get")).Should().BeTrue(); Subject.ShouldProxyBeBypassed(settings, new HttpUri("http://localhost:8654/get")).Should().BeTrue(); + Subject.ShouldProxyBeBypassed(settings, new HttpUri("http://172.21.0.1:8989/api/v3/indexer/schema")).Should().BeTrue(); } [Test] @@ -31,6 +32,7 @@ namespace NzbDrone.Core.Test.Http var settings = GetProxySettings(); Subject.ShouldProxyBeBypassed(settings, new HttpUri("http://bing.com/get")).Should().BeFalse(); + Subject.ShouldProxyBeBypassed(settings, new HttpUri("http://172.3.0.1:8989/api/v3/indexer/schema")).Should().BeFalse(); } } } diff --git a/src/NzbDrone.Core.Test/IndexerTests/AnimeBytesTests/AnimeBytesFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/AnimeBytesTests/AnimeBytesFixture.cs index a2453592f..ae7eaa762 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/AnimeBytesTests/AnimeBytesFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/AnimeBytesTests/AnimeBytesFixture.cs @@ -122,7 +122,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AnimeBytesTests var fifthTorrentInfo = releases.ElementAt(28) as TorrentInfo; - fifthTorrentInfo.Title.Should().Be("[-ZR-] Dr. STONE: STONE WARS S02 [Web][MKV][h264][1080p][AAC 2.0][Dual Audio][Softsubs (-ZR-)]"); + fifthTorrentInfo.Title.Should().Be("[-ZR-] Dr. STONE: STONE WARS 2021 S02 [Web][MKV][h264][1080p][AAC 2.0][Dual Audio][Softsubs (-ZR-)]"); fifthTorrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent); fifthTorrentInfo.DownloadUrl.Should().Be("https://animebytes.tv/torrent/944509/download/somepass"); fifthTorrentInfo.InfoUrl.Should().Be("https://animebytes.tv/torrent/944509/group"); diff --git a/src/NzbDrone.Core.Test/IndexerTests/HDBitsTests/HDBitsFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/HDBitsTests/HDBitsFixture.cs index 39d628d79..06326d162 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/HDBitsTests/HDBitsFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/HDBitsTests/HDBitsFixture.cs @@ -26,15 +26,15 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests [SetUp] public void Setup() { - Subject.Definition = new IndexerDefinition() + Subject.Definition = new IndexerDefinition { Name = "HdBits", - Settings = new HDBitsSettings() { ApiKey = "fakekey" } + Settings = new HDBitsSettings { ApiKey = "fakekey" } }; _movieSearchCriteria = new MovieSearchCriteria { - Categories = new int[] { 2000, 2010 }, + Categories = new[] { 2000, 2010 }, ImdbId = "0076759" }; } @@ -52,7 +52,7 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests var torrents = (await Subject.Fetch(_movieSearchCriteria)).Releases; torrents.Should().HaveCount(2); - torrents.First().Should().BeOfType(); + torrents.First().Should().BeOfType(); var first = torrents.First() as TorrentInfo; diff --git a/src/NzbDrone.Core.Test/IndexerTests/RedactedTests/RedactedFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/RedactedTests/RedactedFixture.cs index 61c664072..d7eb35cd1 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/RedactedTests/RedactedFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/RedactedTests/RedactedFixture.cs @@ -46,8 +46,8 @@ namespace NzbDrone.Core.Test.IndexerTests.RedactedTests torrentInfo.Title.Should().Be("Red Hot Chili Peppers - Californication (1999) [Album] [US / Reissue 2020] [FLAC 24bit Lossless / Vinyl]"); torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent); - torrentInfo.DownloadUrl.Should().Be("https://redacted.ch/ajax.php?action=download&id=3892313"); - torrentInfo.InfoUrl.Should().Be("https://redacted.ch/torrents.php?id=16720&torrentid=3892313"); + torrentInfo.DownloadUrl.Should().Be("https://redacted.sh/ajax.php?action=download&id=3892313"); + torrentInfo.InfoUrl.Should().Be("https://redacted.sh/torrents.php?id=16720&torrentid=3892313"); torrentInfo.CommentUrl.Should().BeNullOrEmpty(); torrentInfo.Indexer.Should().Be(Subject.Definition.Name); torrentInfo.PublishDate.Should().Be(DateTime.Parse("2022-12-17 08:02:35")); diff --git a/src/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs b/src/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs index ce2c446e6..5f2aa2662 100644 --- a/src/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs +++ b/src/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs @@ -35,18 +35,18 @@ namespace NzbDrone.Core.Test.UpdateTests { _updatePackage = new UpdatePackage { - FileName = "NzbDrone.develop.2.0.0.0.tar.gz", + FileName = "NzbDrone.develop.1.0.0.0.tar.gz", Url = "http://download.sonarr.tv/v2/develop/mono/NzbDrone.develop.tar.gz", - Version = new Version("2.0.0.0") + Version = new Version("1.0.0.0") }; } else { _updatePackage = new UpdatePackage { - FileName = "NzbDrone.develop.2.0.0.0.zip", + FileName = "NzbDrone.develop.1.0.0.0.zip", Url = "http://download.sonarr.tv/v2/develop/windows/NzbDrone.develop.zip", - Version = new Version("2.0.0.0") + Version = new Version("1.0.0.0") }; } @@ -90,17 +90,6 @@ namespace NzbDrone.Core.Test.UpdateTests .Returns(true); } - [Test] - public void should_not_update_if_inside_docker() - { - Mocker.GetMock().Setup(x => x.IsDocker).Returns(true); - - Subject.Execute(new ApplicationUpdateCommand()); - - Mocker.GetMock() - .Verify(c => c.Start(It.IsAny(), It.Is(s => s.StartsWith("12")), null, null, null), Times.Never()); - } - [Test] public void should_delete_sandbox_before_update_if_folder_exists() { @@ -338,6 +327,28 @@ namespace NzbDrone.Core.Test.UpdateTests .Verify(v => v.SaveConfigDictionary(It.Is>(d => d.ContainsKey("Branch") && (string)d["Branch"] == "fake")), Times.Once()); } + [Test] + public void should_not_update_with_built_in_updater_inside_docker_container() + { + Mocker.GetMock().Setup(x => x.PackageUpdateMechanism).Returns(UpdateMechanism.Docker); + + Subject.Execute(new ApplicationUpdateCommand()); + + Mocker.GetMock() + .Verify(c => c.Start(It.IsAny(), It.Is(s => s.StartsWith("12")), null, null, null), Times.Never()); + } + + [Test] + public void should_not_update_with_built_in_updater_when_external_updater_is_configured() + { + Mocker.GetMock().Setup(x => x.IsExternalUpdateMechanism).Returns(true); + + Subject.Execute(new ApplicationUpdateCommand()); + + Mocker.GetMock() + .Verify(c => c.Start(It.IsAny(), It.Is(s => s.StartsWith("12")), null, null, null), Times.Never()); + } + [TearDown] public void TearDown() { diff --git a/src/NzbDrone.Core/Applications/ApplicationBase.cs b/src/NzbDrone.Core/Applications/ApplicationBase.cs index e055d37aa..b4d32f054 100644 --- a/src/NzbDrone.Core/Applications/ApplicationBase.cs +++ b/src/NzbDrone.Core/Applications/ApplicationBase.cs @@ -11,6 +11,8 @@ namespace NzbDrone.Core.Applications public abstract class ApplicationBase : IApplication where TSettings : IProviderConfig, new() { + private readonly IIndexerFactory _indexerFactory; + protected readonly IAppIndexerMapService _appIndexerMapService; protected readonly Logger _logger; @@ -27,9 +29,10 @@ namespace NzbDrone.Core.Applications protected TSettings Settings => (TSettings)Definition.Settings; - public ApplicationBase(IAppIndexerMapService appIndexerMapService, Logger logger) + public ApplicationBase(IAppIndexerMapService appIndexerMapService, IIndexerFactory indexerFactory, Logger logger) { _appIndexerMapService = appIndexerMapService; + _indexerFactory = indexerFactory; _logger = logger; } @@ -62,5 +65,17 @@ namespace NzbDrone.Core.Applications { return null; } + + protected IndexerCapabilities GetIndexerCapabilities(IndexerDefinition indexer) + { + try + { + return _indexerFactory.GetInstance(indexer).GetCapabilities(); + } + catch (Exception) + { + return indexer.Capabilities; + } + } } } diff --git a/src/NzbDrone.Core/Applications/ApplicationFactory.cs b/src/NzbDrone.Core/Applications/ApplicationFactory.cs index 492710fee..2ecd2e78b 100644 --- a/src/NzbDrone.Core/Applications/ApplicationFactory.cs +++ b/src/NzbDrone.Core/Applications/ApplicationFactory.cs @@ -53,7 +53,7 @@ namespace NzbDrone.Core.Applications foreach (var application in applications) { - if (blockedApplications.TryGetValue(application.Definition.Id, out var blockedApplicationStatus)) + if (blockedApplications.TryGetValue(application.Definition.Id, out var blockedApplicationStatus) && blockedApplicationStatus.DisabledTill.HasValue) { _logger.Debug("Temporarily ignoring application {0} till {1} due to recent failures.", application.Definition.Name, blockedApplicationStatus.DisabledTill.Value.ToLocalTime()); continue; diff --git a/src/NzbDrone.Core/Applications/ApplicationService.cs b/src/NzbDrone.Core/Applications/ApplicationService.cs index 97e627fd9..0a5a81fbd 100644 --- a/src/NzbDrone.Core/Applications/ApplicationService.cs +++ b/src/NzbDrone.Core/Applications/ApplicationService.cs @@ -127,6 +127,8 @@ namespace NzbDrone.Core.Applications private void SyncIndexers(List applications, List indexers, bool removeRemote = false, bool forceSync = false) { + var sortedIndexers = indexers.OrderBy(i => i.Name, StringComparer.OrdinalIgnoreCase).ToList(); + foreach (var app in applications) { var indexerMappings = _appIndexerMapService.GetMappingsForApp(app.Definition.Id); @@ -157,7 +159,7 @@ namespace NzbDrone.Core.Applications } } - foreach (var indexer in indexers) + foreach (var indexer in sortedIndexers) { var definition = indexer; diff --git a/src/NzbDrone.Core/Applications/LazyLibrarian/LazyLibrarian.cs b/src/NzbDrone.Core/Applications/LazyLibrarian/LazyLibrarian.cs index fcf2f0824..108972d6e 100644 --- a/src/NzbDrone.Core/Applications/LazyLibrarian/LazyLibrarian.cs +++ b/src/NzbDrone.Core/Applications/LazyLibrarian/LazyLibrarian.cs @@ -15,14 +15,12 @@ namespace NzbDrone.Core.Applications.LazyLibrarian private readonly ILazyLibrarianV1Proxy _lazyLibrarianV1Proxy; private readonly IConfigFileProvider _configFileProvider; - private readonly IIndexerFactory _indexerFactory; public LazyLibrarian(ILazyLibrarianV1Proxy lazyLibrarianV1Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, IIndexerFactory indexerFactory, Logger logger) - : base(appIndexerMapService, logger) + : base(appIndexerMapService, indexerFactory, logger) { _lazyLibrarianV1Proxy = lazyLibrarianV1Proxy; _configFileProvider = configFileProvider; - _indexerFactory = indexerFactory; } public override ValidationResult Test() @@ -67,7 +65,7 @@ namespace NzbDrone.Core.Applications.LazyLibrarian public override void AddIndexer(IndexerDefinition indexer) { - var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities(); + var indexerCapabilities = GetIndexerCapabilities(indexer); if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty()) { @@ -111,7 +109,7 @@ namespace NzbDrone.Core.Applications.LazyLibrarian { _logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id); - var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities(); + var indexerCapabilities = GetIndexerCapabilities(indexer); var appMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id); var indexerMapping = appMappings.FirstOrDefault(m => m.IndexerId == indexer.Id); var indexerProps = indexerMapping.RemoteIndexerName.Split(","); @@ -167,6 +165,13 @@ namespace NzbDrone.Core.Applications.LazyLibrarian Priority = indexer.Priority }; + if (indexer.Protocol == DownloadProtocol.Torrent) + { + lazyLibrarianIndexer.MinimumSeeders = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.AppMinimumSeeders ?? indexer.AppProfile.Value.MinimumSeeders; + lazyLibrarianIndexer.SeedRatio = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.SeedRatio.GetValueOrDefault(); + lazyLibrarianIndexer.SeedTime = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.SeedTime.GetValueOrDefault(); + } + return lazyLibrarianIndexer; } } diff --git a/src/NzbDrone.Core/Applications/LazyLibrarian/LazyLibrarianIndexer.cs b/src/NzbDrone.Core/Applications/LazyLibrarian/LazyLibrarianIndexer.cs index bc20c3ddf..b13403aac 100644 --- a/src/NzbDrone.Core/Applications/LazyLibrarian/LazyLibrarianIndexer.cs +++ b/src/NzbDrone.Core/Applications/LazyLibrarian/LazyLibrarianIndexer.cs @@ -31,6 +31,9 @@ namespace NzbDrone.Core.Applications.LazyLibrarian public string Altername { get; set; } public LazyLibrarianProviderType Type { get; set; } public int Priority { get; set; } + public double SeedRatio { get; set; } + public int SeedTime { get; set; } + public int MinimumSeeders { get; set; } public bool Equals(LazyLibrarianIndexer other) { @@ -45,7 +48,10 @@ namespace NzbDrone.Core.Applications.LazyLibrarian other.Categories == Categories && other.Enabled == Enabled && other.Altername == Altername && - other.Priority == Priority; + other.Priority == Priority && + other.SeedRatio == SeedRatio && + other.SeedTime == SeedTime && + other.MinimumSeeders == MinimumSeeders; } } } diff --git a/src/NzbDrone.Core/Applications/LazyLibrarian/LazyLibrarianV1Proxy.cs b/src/NzbDrone.Core/Applications/LazyLibrarian/LazyLibrarianV1Proxy.cs index 563f9ed3b..f4c6da138 100644 --- a/src/NzbDrone.Core/Applications/LazyLibrarian/LazyLibrarianV1Proxy.cs +++ b/src/NzbDrone.Core/Applications/LazyLibrarian/LazyLibrarianV1Proxy.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Net.Http; using FluentValidation.Results; @@ -96,6 +97,13 @@ namespace NzbDrone.Core.Applications.LazyLibrarian { "dlpriority", CalculatePriority(indexer.Priority).ToString() } }; + if (indexer.Type == LazyLibrarianProviderType.Torznab) + { + parameters.Add("seeders", indexer.MinimumSeeders.ToString()); + parameters.Add("seed_ratio", indexer.SeedRatio.ToString(CultureInfo.InvariantCulture)); + parameters.Add("seed_duration", indexer.SeedTime.ToString()); + } + var request = BuildRequest(settings, "/api", "addProvider", HttpMethod.Get, parameters); CheckForError(Execute(request)); return indexer; @@ -115,6 +123,13 @@ namespace NzbDrone.Core.Applications.LazyLibrarian { "dlpriority", CalculatePriority(indexer.Priority).ToString() } }; + if (indexer.Type == LazyLibrarianProviderType.Torznab) + { + parameters.Add("seeders", indexer.MinimumSeeders.ToString()); + parameters.Add("seed_ratio", indexer.SeedRatio.ToString(CultureInfo.InvariantCulture)); + parameters.Add("seed_duration", indexer.SeedTime.ToString()); + } + var request = BuildRequest(settings, "/api", "changeProvider", HttpMethod.Get, parameters); CheckForError(Execute(request)); return indexer; diff --git a/src/NzbDrone.Core/Applications/Lidarr/Lidarr.cs b/src/NzbDrone.Core/Applications/Lidarr/Lidarr.cs index 80ae02319..19c842f5c 100644 --- a/src/NzbDrone.Core/Applications/Lidarr/Lidarr.cs +++ b/src/NzbDrone.Core/Applications/Lidarr/Lidarr.cs @@ -21,15 +21,13 @@ namespace NzbDrone.Core.Applications.Lidarr private readonly ILidarrV1Proxy _lidarrV1Proxy; private readonly ICached> _schemaCache; private readonly IConfigFileProvider _configFileProvider; - private readonly IIndexerFactory _indexerFactory; public Lidarr(ICacheManager cacheManager, ILidarrV1Proxy lidarrV1Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, IIndexerFactory indexerFactory, Logger logger) - : base(appIndexerMapService, logger) + : base(appIndexerMapService, indexerFactory, logger) { _schemaCache = cacheManager.GetCache>(GetType()); _lidarrV1Proxy = lidarrV1Proxy; _configFileProvider = configFileProvider; - _indexerFactory = indexerFactory; } public override ValidationResult Test() @@ -66,6 +64,7 @@ namespace NzbDrone.Core.Applications.Lidarr failures.AddIfNotNull(new ValidationFailure("ProwlarrUrl", "Prowlarr URL is invalid, Lidarr cannot connect to Prowlarr")); break; case HttpStatusCode.SeeOther: + case HttpStatusCode.TemporaryRedirect: _logger.Warn(ex, "Lidarr returned redirect and is invalid"); failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Lidarr URL is invalid, Prowlarr cannot connect to Lidarr - are you missing a URL base?")); break; @@ -120,7 +119,7 @@ namespace NzbDrone.Core.Applications.Lidarr public override void AddIndexer(IndexerDefinition indexer) { - var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities(); + var indexerCapabilities = GetIndexerCapabilities(indexer); if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty()) { @@ -163,7 +162,7 @@ namespace NzbDrone.Core.Applications.Lidarr { _logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id); - var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities(); + var indexerCapabilities = GetIndexerCapabilities(indexer); var appMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id); var indexerMapping = appMappings.FirstOrDefault(m => m.IndexerId == indexer.Id); diff --git a/src/NzbDrone.Core/Applications/Lidarr/LidarrV1Proxy.cs b/src/NzbDrone.Core/Applications/Lidarr/LidarrV1Proxy.cs index 9b7f37685..3fcb337c0 100644 --- a/src/NzbDrone.Core/Applications/Lidarr/LidarrV1Proxy.cs +++ b/src/NzbDrone.Core/Applications/Lidarr/LidarrV1Proxy.cs @@ -166,6 +166,7 @@ namespace NzbDrone.Core.Applications.Lidarr _logger.Error(ex, "Invalid Request"); break; case HttpStatusCode.SeeOther: + case HttpStatusCode.TemporaryRedirect: _logger.Warn(ex, "App returned redirect and is invalid. Check App URL"); break; case HttpStatusCode.NotFound: diff --git a/src/NzbDrone.Core/Applications/Mylar/Mylar.cs b/src/NzbDrone.Core/Applications/Mylar/Mylar.cs index 3724aaf1c..e9fd9ffe7 100644 --- a/src/NzbDrone.Core/Applications/Mylar/Mylar.cs +++ b/src/NzbDrone.Core/Applications/Mylar/Mylar.cs @@ -15,14 +15,12 @@ namespace NzbDrone.Core.Applications.Mylar private readonly IMylarV3Proxy _mylarV3Proxy; private readonly IConfigFileProvider _configFileProvider; - private readonly IIndexerFactory _indexerFactory; public Mylar(IMylarV3Proxy mylarV3Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, IIndexerFactory indexerFactory, Logger logger) - : base(appIndexerMapService, logger) + : base(appIndexerMapService, indexerFactory, logger) { _mylarV3Proxy = mylarV3Proxy; _configFileProvider = configFileProvider; - _indexerFactory = indexerFactory; } public override ValidationResult Test() @@ -67,7 +65,7 @@ namespace NzbDrone.Core.Applications.Mylar public override void AddIndexer(IndexerDefinition indexer) { - var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities(); + var indexerCapabilities = GetIndexerCapabilities(indexer); if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty()) { @@ -111,7 +109,7 @@ namespace NzbDrone.Core.Applications.Mylar { _logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id); - var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities(); + var indexerCapabilities = GetIndexerCapabilities(indexer); var appMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id); var indexerMapping = appMappings.FirstOrDefault(m => m.IndexerId == indexer.Id); var indexerProps = indexerMapping.RemoteIndexerName.Split(","); diff --git a/src/NzbDrone.Core/Applications/Radarr/Radarr.cs b/src/NzbDrone.Core/Applications/Radarr/Radarr.cs index 43cb1ee51..85b9c4a3b 100644 --- a/src/NzbDrone.Core/Applications/Radarr/Radarr.cs +++ b/src/NzbDrone.Core/Applications/Radarr/Radarr.cs @@ -21,15 +21,13 @@ namespace NzbDrone.Core.Applications.Radarr private readonly IRadarrV3Proxy _radarrV3Proxy; private readonly ICached> _schemaCache; private readonly IConfigFileProvider _configFileProvider; - private readonly IIndexerFactory _indexerFactory; public Radarr(ICacheManager cacheManager, IRadarrV3Proxy radarrV3Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, IIndexerFactory indexerFactory, Logger logger) - : base(appIndexerMapService, logger) + : base(appIndexerMapService, indexerFactory, logger) { _schemaCache = cacheManager.GetCache>(GetType()); _radarrV3Proxy = radarrV3Proxy; _configFileProvider = configFileProvider; - _indexerFactory = indexerFactory; } public override ValidationResult Test() @@ -66,6 +64,7 @@ namespace NzbDrone.Core.Applications.Radarr failures.AddIfNotNull(new ValidationFailure("ProwlarrUrl", "Prowlarr URL is invalid, Radarr cannot connect to Prowlarr")); break; case HttpStatusCode.SeeOther: + case HttpStatusCode.TemporaryRedirect: _logger.Warn(ex, "Radarr returned redirect and is invalid"); failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Radarr URL is invalid, Prowlarr cannot connect to Radarr - are you missing a URL base?")); break; @@ -120,7 +119,7 @@ namespace NzbDrone.Core.Applications.Radarr public override void AddIndexer(IndexerDefinition indexer) { - var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities(); + var indexerCapabilities = GetIndexerCapabilities(indexer); if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty()) { @@ -163,7 +162,7 @@ namespace NzbDrone.Core.Applications.Radarr { _logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id); - var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities(); + var indexerCapabilities = GetIndexerCapabilities(indexer); var appMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id); var indexerMapping = appMappings.FirstOrDefault(m => m.IndexerId == indexer.Id); diff --git a/src/NzbDrone.Core/Applications/Radarr/RadarrV3Proxy.cs b/src/NzbDrone.Core/Applications/Radarr/RadarrV3Proxy.cs index e74baad21..d431856aa 100644 --- a/src/NzbDrone.Core/Applications/Radarr/RadarrV3Proxy.cs +++ b/src/NzbDrone.Core/Applications/Radarr/RadarrV3Proxy.cs @@ -179,6 +179,7 @@ namespace NzbDrone.Core.Applications.Radarr _logger.Error(ex, "Invalid Request"); break; case HttpStatusCode.SeeOther: + case HttpStatusCode.TemporaryRedirect: _logger.Warn(ex, "App returned redirect and is invalid. Check App URL"); break; case HttpStatusCode.NotFound: diff --git a/src/NzbDrone.Core/Applications/Readarr/Readarr.cs b/src/NzbDrone.Core/Applications/Readarr/Readarr.cs index 12a6dc7d1..1fc6742ae 100644 --- a/src/NzbDrone.Core/Applications/Readarr/Readarr.cs +++ b/src/NzbDrone.Core/Applications/Readarr/Readarr.cs @@ -21,15 +21,13 @@ namespace NzbDrone.Core.Applications.Readarr private readonly ICached> _schemaCache; private readonly IReadarrV1Proxy _readarrV1Proxy; private readonly IConfigFileProvider _configFileProvider; - private readonly IIndexerFactory _indexerFactory; public Readarr(ICacheManager cacheManager, IReadarrV1Proxy readarrV1Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, IIndexerFactory indexerFactory, Logger logger) - : base(appIndexerMapService, logger) + : base(appIndexerMapService, indexerFactory, logger) { _schemaCache = cacheManager.GetCache>(GetType()); _readarrV1Proxy = readarrV1Proxy; _configFileProvider = configFileProvider; - _indexerFactory = indexerFactory; } public override ValidationResult Test() @@ -66,6 +64,7 @@ namespace NzbDrone.Core.Applications.Readarr failures.AddIfNotNull(new ValidationFailure("ProwlarrUrl", "Prowlarr URL is invalid, Readarr cannot connect to Prowlarr")); break; case HttpStatusCode.SeeOther: + case HttpStatusCode.TemporaryRedirect: _logger.Warn(ex, "Readarr returned redirect and is invalid"); failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Readarr URL is invalid, Prowlarr cannot connect to Readarr - are you missing a URL base?")); break; @@ -120,7 +119,7 @@ namespace NzbDrone.Core.Applications.Readarr public override void AddIndexer(IndexerDefinition indexer) { - var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities(); + var indexerCapabilities = GetIndexerCapabilities(indexer); if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty()) { @@ -163,7 +162,7 @@ namespace NzbDrone.Core.Applications.Readarr { _logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id); - var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities(); + var indexerCapabilities = GetIndexerCapabilities(indexer); var appMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id); var indexerMapping = appMappings.FirstOrDefault(m => m.IndexerId == indexer.Id); diff --git a/src/NzbDrone.Core/Applications/Readarr/ReadarrV1Proxy.cs b/src/NzbDrone.Core/Applications/Readarr/ReadarrV1Proxy.cs index 71e8a2c45..899ef79b6 100644 --- a/src/NzbDrone.Core/Applications/Readarr/ReadarrV1Proxy.cs +++ b/src/NzbDrone.Core/Applications/Readarr/ReadarrV1Proxy.cs @@ -153,6 +153,7 @@ namespace NzbDrone.Core.Applications.Readarr _logger.Error(ex, "Invalid Request"); break; case HttpStatusCode.SeeOther: + case HttpStatusCode.TemporaryRedirect: _logger.Warn(ex, "App returned redirect and is invalid. Check App URL"); break; case HttpStatusCode.NotFound: diff --git a/src/NzbDrone.Core/Applications/Sonarr/Sonarr.cs b/src/NzbDrone.Core/Applications/Sonarr/Sonarr.cs index 509fda8e1..6e5284fc7 100644 --- a/src/NzbDrone.Core/Applications/Sonarr/Sonarr.cs +++ b/src/NzbDrone.Core/Applications/Sonarr/Sonarr.cs @@ -21,15 +21,13 @@ namespace NzbDrone.Core.Applications.Sonarr private readonly ICached> _schemaCache; private readonly ISonarrV3Proxy _sonarrV3Proxy; private readonly IConfigFileProvider _configFileProvider; - private readonly IIndexerFactory _indexerFactory; public Sonarr(ICacheManager cacheManager, ISonarrV3Proxy sonarrV3Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, IIndexerFactory indexerFactory, Logger logger) - : base(appIndexerMapService, logger) + : base(appIndexerMapService, indexerFactory, logger) { _schemaCache = cacheManager.GetCache>(GetType()); _sonarrV3Proxy = sonarrV3Proxy; _configFileProvider = configFileProvider; - _indexerFactory = indexerFactory; } public override ValidationResult Test() @@ -66,6 +64,7 @@ namespace NzbDrone.Core.Applications.Sonarr failures.AddIfNotNull(new ValidationFailure("ProwlarrUrl", "Prowlarr URL is invalid, Sonarr cannot connect to Prowlarr")); break; case HttpStatusCode.SeeOther: + case HttpStatusCode.TemporaryRedirect: _logger.Warn(ex, "Sonarr returned redirect and is invalid"); failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Sonarr URL is invalid, Prowlarr cannot connect to Sonarr - are you missing a URL base?")); break; @@ -124,7 +123,7 @@ namespace NzbDrone.Core.Applications.Sonarr public override void AddIndexer(IndexerDefinition indexer) { - var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities(); + var indexerCapabilities = GetIndexerCapabilities(indexer); if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty() && indexerCapabilities.Categories.SupportedCategories(Settings.AnimeSyncCategories.ToArray()).Empty()) @@ -168,7 +167,7 @@ namespace NzbDrone.Core.Applications.Sonarr { _logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id); - var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities(); + var indexerCapabilities = GetIndexerCapabilities(indexer); var appMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id); var indexerMapping = appMappings.FirstOrDefault(m => m.IndexerId == indexer.Id); diff --git a/src/NzbDrone.Core/Applications/Sonarr/SonarrSettings.cs b/src/NzbDrone.Core/Applications/Sonarr/SonarrSettings.cs index 87a45330f..95b52bab0 100644 --- a/src/NzbDrone.Core/Applications/Sonarr/SonarrSettings.cs +++ b/src/NzbDrone.Core/Applications/Sonarr/SonarrSettings.cs @@ -44,7 +44,7 @@ namespace NzbDrone.Core.Applications.Sonarr public IEnumerable AnimeSyncCategories { get; set; } [FieldDefinition(5, Label = "Sync Anime Standard Format Search", Type = FieldType.Checkbox, HelpText = "Sync also searching for anime using the standard numbering", Advanced = true)] - public bool SyncAnimeStandardFormatSearch { get; set; } + public bool SyncAnimeStandardFormatSearch { get; set; } = true; [FieldDefinition(6, Type = FieldType.Checkbox, Label = "ApplicationSettingsSyncRejectBlocklistedTorrentHashes", HelpText = "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText", Advanced = true)] public bool SyncRejectBlocklistedTorrentHashesWhileGrabbing { get; set; } diff --git a/src/NzbDrone.Core/Applications/Sonarr/SonarrV3Proxy.cs b/src/NzbDrone.Core/Applications/Sonarr/SonarrV3Proxy.cs index 48dcea40d..f92043c99 100644 --- a/src/NzbDrone.Core/Applications/Sonarr/SonarrV3Proxy.cs +++ b/src/NzbDrone.Core/Applications/Sonarr/SonarrV3Proxy.cs @@ -166,6 +166,7 @@ namespace NzbDrone.Core.Applications.Sonarr _logger.Error(ex, "Invalid Request"); break; case HttpStatusCode.SeeOther: + case HttpStatusCode.TemporaryRedirect: _logger.Warn(ex, "App returned redirect and is invalid. Check App URL"); break; case HttpStatusCode.NotFound: diff --git a/src/NzbDrone.Core/Applications/Whisparr/Whisparr.cs b/src/NzbDrone.Core/Applications/Whisparr/Whisparr.cs index 75d98045a..0c149fc7c 100644 --- a/src/NzbDrone.Core/Applications/Whisparr/Whisparr.cs +++ b/src/NzbDrone.Core/Applications/Whisparr/Whisparr.cs @@ -21,15 +21,13 @@ namespace NzbDrone.Core.Applications.Whisparr private readonly IWhisparrV3Proxy _whisparrV3Proxy; private readonly ICached> _schemaCache; private readonly IConfigFileProvider _configFileProvider; - private readonly IIndexerFactory _indexerFactory; public Whisparr(ICacheManager cacheManager, IWhisparrV3Proxy whisparrV3Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, IIndexerFactory indexerFactory, Logger logger) - : base(appIndexerMapService, logger) + : base(appIndexerMapService, indexerFactory, logger) { _schemaCache = cacheManager.GetCache>(GetType()); _whisparrV3Proxy = whisparrV3Proxy; _configFileProvider = configFileProvider; - _indexerFactory = indexerFactory; } public override ValidationResult Test() @@ -66,6 +64,7 @@ namespace NzbDrone.Core.Applications.Whisparr failures.AddIfNotNull(new ValidationFailure("ProwlarrUrl", "Prowlarr URL is invalid, Whisparr cannot connect to Prowlarr")); break; case HttpStatusCode.SeeOther: + case HttpStatusCode.TemporaryRedirect: _logger.Warn(ex, "Whisparr returned redirect and is invalid"); failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Whisparr URL is invalid, Prowlarr cannot connect to Whisparr - are you missing a URL base?")); break; @@ -120,7 +119,7 @@ namespace NzbDrone.Core.Applications.Whisparr public override void AddIndexer(IndexerDefinition indexer) { - var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities(); + var indexerCapabilities = GetIndexerCapabilities(indexer); if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty()) { @@ -163,7 +162,7 @@ namespace NzbDrone.Core.Applications.Whisparr { _logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id); - var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities(); + var indexerCapabilities = GetIndexerCapabilities(indexer); var appMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id); var indexerMapping = appMappings.FirstOrDefault(m => m.IndexerId == indexer.Id); diff --git a/src/NzbDrone.Core/Applications/Whisparr/WhisparrV3Proxy.cs b/src/NzbDrone.Core/Applications/Whisparr/WhisparrV3Proxy.cs index 530005c5f..e2ee60524 100644 --- a/src/NzbDrone.Core/Applications/Whisparr/WhisparrV3Proxy.cs +++ b/src/NzbDrone.Core/Applications/Whisparr/WhisparrV3Proxy.cs @@ -151,6 +151,7 @@ namespace NzbDrone.Core.Applications.Whisparr _logger.Error(ex, "Invalid Request"); break; case HttpStatusCode.SeeOther: + case HttpStatusCode.TemporaryRedirect: _logger.Warn(ex, "App returned redirect and is invalid. Check App URL"); break; case HttpStatusCode.NotFound: diff --git a/src/NzbDrone.Core/Backup/BackupService.cs b/src/NzbDrone.Core/Backup/BackupService.cs index 14fc91efc..051d045bb 100644 --- a/src/NzbDrone.Core/Backup/BackupService.cs +++ b/src/NzbDrone.Core/Backup/BackupService.cs @@ -66,12 +66,19 @@ namespace NzbDrone.Core.Backup { _logger.ProgressInfo("Starting Backup"); + var backupFolder = GetBackupFolder(backupType); + _diskProvider.EnsureFolder(_backupTempFolder); - _diskProvider.EnsureFolder(GetBackupFolder(backupType)); + _diskProvider.EnsureFolder(backupFolder); + + if (!_diskProvider.FolderWritable(backupFolder)) + { + throw new UnauthorizedAccessException($"Backup folder {backupFolder} is not writable"); + } var dateNow = DateTime.Now; var backupFilename = $"prowlarr_backup_v{BuildInfo.Version}_{dateNow:yyyy.MM.dd_HH.mm.ss}.zip"; - var backupPath = Path.Combine(GetBackupFolder(backupType), backupFilename); + var backupPath = Path.Combine(backupFolder, backupFilename); Cleanup(); diff --git a/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs b/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs index bd4faa4bc..f4715b203 100644 --- a/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs +++ b/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs @@ -10,6 +10,7 @@ using NzbDrone.Common.Cache; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; +using NzbDrone.Common.Instrumentation; using NzbDrone.Common.Options; using NzbDrone.Core.Authentication; using NzbDrone.Core.Configuration.Events; @@ -38,8 +39,10 @@ namespace NzbDrone.Core.Configuration bool AnalyticsEnabled { get; } string LogLevel { get; } string ConsoleLogLevel { get; } + ConsoleLogFormat ConsoleLogFormat { get; } bool LogSql { get; } int LogRotate { get; } + int LogSizeLimit { get; } bool FilterSentryEvents { get; } string Branch { get; } string ApiKey { get; } @@ -62,6 +65,7 @@ namespace NzbDrone.Core.Configuration string PostgresPassword { get; } string PostgresMainDb { get; } string PostgresLogDb { get; } + bool TrustCgnatIpAddresses { get; } } public class ConfigFileProvider : IConfigFileProvider @@ -222,9 +226,14 @@ namespace NzbDrone.Core.Configuration public string Branch => _updateOptions.Branch ?? GetValue("Branch", "master").ToLowerInvariant(); - public string LogLevel => _logOptions.Level ?? GetValue("LogLevel", "info").ToLowerInvariant(); + public string LogLevel => _logOptions.Level ?? GetValue("LogLevel", "debug").ToLowerInvariant(); public string ConsoleLogLevel => _logOptions.ConsoleLevel ?? GetValue("ConsoleLogLevel", string.Empty, persist: false); + public ConsoleLogFormat ConsoleLogFormat => + Enum.TryParse(_logOptions.ConsoleFormat, out var enumValue) + ? enumValue + : GetValueEnum("ConsoleLogFormat", ConsoleLogFormat.Standard, false); + public string Theme => _appOptions.Theme ?? GetValue("Theme", "auto", persist: false); public string PostgresHost => _postgresOptions?.Host ?? GetValue("PostgresHost", string.Empty, persist: false); @@ -237,6 +246,7 @@ namespace NzbDrone.Core.Configuration public bool LogDbEnabled => _logOptions.DbEnabled ?? GetValueBoolean("LogDbEnabled", true, persist: false); public bool LogSql => _logOptions.Sql ?? GetValueBoolean("LogSql", false, persist: false); public int LogRotate => _logOptions.Rotate ?? GetValueInt("LogRotate", 50, persist: false); + public int LogSizeLimit => Math.Min(Math.Max(_logOptions.SizeLimit ?? GetValueInt("LogSizeLimit", 1, persist: false), 0), 10); public bool FilterSentryEvents => _logOptions.FilterSentryEvents ?? GetValueBoolean("FilterSentryEvents", true, persist: false); public string SslCertPath => _serverOptions.SslCertPath ?? GetValue("SslCertPath", ""); public string SslCertPassword => _serverOptions.SslCertPassword ?? GetValue("SslCertPassword", ""); @@ -245,7 +255,7 @@ namespace NzbDrone.Core.Configuration { get { - var urlBase = _serverOptions.UrlBase ?? GetValue("UrlBase", "").Trim('/'); + var urlBase = (_serverOptions.UrlBase ?? GetValue("UrlBase", "")).Trim('/'); if (urlBase.IsNullOrWhiteSpace()) { @@ -264,7 +274,7 @@ namespace NzbDrone.Core.Configuration { var instanceName = _appOptions.InstanceName ?? GetValue("InstanceName", BuildInfo.AppName); - if (instanceName.ContainsIgnoreCase(BuildInfo.AppName)) + if (instanceName.Contains(BuildInfo.AppName, StringComparison.OrdinalIgnoreCase)) { return instanceName; } @@ -273,7 +283,7 @@ namespace NzbDrone.Core.Configuration } } - public bool UpdateAutomatically => _updateOptions.Automatically ?? GetValueBoolean("UpdateAutomatically", false, false); + public bool UpdateAutomatically => _updateOptions.Automatically ?? GetValueBoolean("UpdateAutomatically", OsInfo.IsWindows, false); public UpdateMechanism UpdateMechanism => Enum.TryParse(_updateOptions.Mechanism, out var enumValue) @@ -375,7 +385,7 @@ namespace NzbDrone.Core.Configuration } // If SSL is enabled and a cert hash is still in the config file or cert path is empty disable SSL - if (EnableSsl && (GetValue("SslCertHash", null).IsNotNullOrWhiteSpace() || SslCertPath.IsNullOrWhiteSpace())) + if (EnableSsl && (GetValue("SslCertHash", string.Empty, false).IsNotNullOrWhiteSpace() || SslCertPath.IsNullOrWhiteSpace())) { SetValue("EnableSsl", false); } @@ -422,13 +432,21 @@ namespace NzbDrone.Core.Configuration throw new InvalidConfigFileException($"{_configFile} is corrupt. Please delete the config file and Prowlarr will recreate it."); } - return XDocument.Parse(_diskProvider.ReadAllText(_configFile)); + var xDoc = XDocument.Parse(_diskProvider.ReadAllText(_configFile)); + var config = xDoc.Descendants(CONFIG_ELEMENT_NAME).ToList(); + + if (config.Count != 1) + { + throw new InvalidConfigFileException($"{_configFile} is invalid. Please delete the config file and Prowlarr will recreate it."); + } + + return xDoc; } - var xDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes")); - xDoc.Add(new XElement(CONFIG_ELEMENT_NAME)); + var newXDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes")); + newXDoc.Add(new XElement(CONFIG_ELEMENT_NAME)); - return xDoc; + return newXDoc; } } catch (XmlException ex) @@ -462,5 +480,7 @@ namespace NzbDrone.Core.Configuration SetValue("ApiKey", GenerateApiKey()); _eventAggregator.PublishEvent(new ApiKeyChangedEvent()); } + + public bool TrustCgnatIpAddresses => _authOptions.TrustCgnatIpAddresses ?? GetValueBoolean("TrustCgnatIpAddresses", false, persist: false); } } diff --git a/src/NzbDrone.Core/Configuration/ConfigService.cs b/src/NzbDrone.Core/Configuration/ConfigService.cs index 208692c97..27a953823 100644 --- a/src/NzbDrone.Core/Configuration/ConfigService.cs +++ b/src/NzbDrone.Core/Configuration/ConfigService.cs @@ -183,6 +183,12 @@ namespace NzbDrone.Core.Configuration public string ApplicationUrl => GetValue("ApplicationUrl", string.Empty); + public bool TrustCgnatIpAddresses + { + get { return GetValueBoolean("TrustCgnatIpAddresses", false); } + set { SetValue("TrustCgnatIpAddresses", value); } + } + private string GetValue(string key) { return GetValue(key, string.Empty); diff --git a/src/NzbDrone.Core/Datastore/BasicRepository.cs b/src/NzbDrone.Core/Datastore/BasicRepository.cs index dc76a5a31..796e277b7 100644 --- a/src/NzbDrone.Core/Datastore/BasicRepository.cs +++ b/src/NzbDrone.Core/Datastore/BasicRepository.cs @@ -254,7 +254,7 @@ namespace NzbDrone.Core.Datastore protected void Delete(SqlBuilder builder) { - var sql = builder.AddDeleteTemplate(typeof(TModel)).LogQuery(); + var sql = builder.AddDeleteTemplate(typeof(TModel)); using (var conn = _database.OpenConnection()) { diff --git a/src/NzbDrone.Core/Datastore/Converters/TimeSpanConverter.cs b/src/NzbDrone.Core/Datastore/Converters/TimeSpanConverter.cs index 902a26009..fdcb227c6 100644 --- a/src/NzbDrone.Core/Datastore/Converters/TimeSpanConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/TimeSpanConverter.cs @@ -2,18 +2,17 @@ using System; using System.Data; using Dapper; -namespace NzbDrone.Core.Datastore.Converters -{ - public class DapperTimeSpanConverter : SqlMapper.TypeHandler - { - public override void SetValue(IDbDataParameter parameter, TimeSpan value) - { - parameter.Value = value.ToString(); - } +namespace NzbDrone.Core.Datastore.Converters; - public override TimeSpan Parse(object value) - { - return TimeSpan.Parse((string)value); - } +public class TimeSpanConverter : SqlMapper.TypeHandler +{ + public override void SetValue(IDbDataParameter parameter, TimeSpan value) + { + parameter.Value = value.ToString(); + } + + public override TimeSpan Parse(object value) + { + return value is string str ? TimeSpan.Parse(str) : TimeSpan.Zero; } } diff --git a/src/NzbDrone.Core/Datastore/Database.cs b/src/NzbDrone.Core/Datastore/Database.cs index 887039bcb..741a22f0b 100644 --- a/src/NzbDrone.Core/Datastore/Database.cs +++ b/src/NzbDrone.Core/Datastore/Database.cs @@ -2,7 +2,6 @@ using System; using System.Data; using System.Data.Common; using System.Data.SQLite; -using System.Text.RegularExpressions; using Dapper; using NLog; using NzbDrone.Common.Instrumentation; @@ -52,9 +51,8 @@ namespace NzbDrone.Core.Datastore { using var db = _datamapperFactory(); var dbConnection = db as DbConnection; - var version = Regex.Replace(dbConnection.ServerVersion, @"\(.*?\)", ""); - return new Version(version); + return DatabaseVersionParser.ParseServerVersion(dbConnection.ServerVersion); } } diff --git a/src/NzbDrone.Core/Datastore/DatabaseVersionParser.cs b/src/NzbDrone.Core/Datastore/DatabaseVersionParser.cs new file mode 100644 index 000000000..ffc77cf18 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/DatabaseVersionParser.cs @@ -0,0 +1,16 @@ +using System; +using System.Text.RegularExpressions; + +namespace NzbDrone.Core.Datastore; + +public static class DatabaseVersionParser +{ + private static readonly Regex VersionRegex = new (@"^[^ ]+", RegexOptions.Compiled); + + public static Version ParseServerVersion(string serverVersion) + { + var match = VersionRegex.Match(serverVersion); + + return match.Success ? new Version(match.Value) : null; + } +} diff --git a/src/NzbDrone.Core/Datastore/Migration/041_gazelle_freeleech_token_options.cs b/src/NzbDrone.Core/Datastore/Migration/041_gazelle_freeleech_token_options.cs new file mode 100644 index 000000000..56a11c732 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/041_gazelle_freeleech_token_options.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using System.Data; +using Dapper; +using FluentMigrator; +using Newtonsoft.Json.Linq; +using NzbDrone.Common.Serializer; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(041)] + public class gazelle_freeleech_token_options : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Execute.WithConnection(MigrateIndexersToTokenOptions); + } + + private void MigrateIndexersToTokenOptions(IDbConnection conn, IDbTransaction tran) + { + var updated = new List(); + + using (var cmd = conn.CreateCommand()) + { + cmd.Transaction = tran; + cmd.CommandText = "SELECT \"Id\", \"Settings\" FROM \"Indexers\" WHERE \"Implementation\" IN ('Orpheus', 'Redacted', 'AlphaRatio', 'BrokenStones', 'CGPeers', 'DICMusic', 'GreatPosterWall', 'SecretCinema')"; + + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var id = reader.GetInt32(0); + var settings = Json.Deserialize(reader.GetString(1)); + + if (settings.ContainsKey("useFreeleechToken") && settings.Value("useFreeleechToken").Type == JTokenType.Boolean) + { + var optionValue = settings.Value("useFreeleechToken") switch + { + true => 2, // Required + _ => 0 // Never + }; + + settings.Remove("useFreeleechToken"); + settings.Add("useFreeleechToken", optionValue); + } + + updated.Add(new + { + Id = id, + Settings = settings.ToJson() + }); + } + } + } + + var updateSql = "UPDATE \"Indexers\" SET \"Settings\" = @Settings WHERE \"Id\" = @Id"; + conn.Execute(updateSql, updated, transaction: tran); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/Migration/042_myanonamouse_freeleech_wedge_options.cs b/src/NzbDrone.Core/Datastore/Migration/042_myanonamouse_freeleech_wedge_options.cs new file mode 100644 index 000000000..5a93488d5 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/042_myanonamouse_freeleech_wedge_options.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using System.Data; +using Dapper; +using FluentMigrator; +using Newtonsoft.Json.Linq; +using NzbDrone.Common.Serializer; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(042)] + public class myanonamouse_freeleech_wedge_options : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Execute.WithConnection(MigrateIndexersToWedgeOptions); + } + + private void MigrateIndexersToWedgeOptions(IDbConnection conn, IDbTransaction tran) + { + var updated = new List(); + + using (var cmd = conn.CreateCommand()) + { + cmd.Transaction = tran; + cmd.CommandText = "SELECT \"Id\", \"Settings\" FROM \"Indexers\" WHERE \"Implementation\" = 'MyAnonamouse'"; + + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var id = reader.GetInt32(0); + var settings = Json.Deserialize(reader.GetString(1)); + + if (settings.ContainsKey("freeleech") && settings.Value("freeleech").Type == JTokenType.Boolean) + { + var optionValue = settings.Value("freeleech") switch + { + true => 2, // Required + _ => 0 // Never + }; + + settings.Remove("freeleech"); + settings.Add("useFreeleechWedge", optionValue); + } + + updated.Add(new + { + Id = id, + Settings = settings.ToJson() + }); + } + } + } + + var updateSql = "UPDATE \"Indexers\" SET \"Settings\" = @Settings WHERE \"Id\" = @Id"; + conn.Execute(updateSql, updated, transaction: tran); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/Migration/Framework/SqliteSchemaDumper.cs b/src/NzbDrone.Core/Datastore/Migration/Framework/SqliteSchemaDumper.cs index d0a661751..a56384466 100644 --- a/src/NzbDrone.Core/Datastore/Migration/Framework/SqliteSchemaDumper.cs +++ b/src/NzbDrone.Core/Datastore/Migration/Framework/SqliteSchemaDumper.cs @@ -219,7 +219,7 @@ namespace NzbDrone.Core.Datastore.Migration.Framework protected virtual IList ReadTables() { - const string sqlCommand = @"SELECT name, sql FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name;"; + const string sqlCommand = @"SELECT name, sql FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '_litestream_%' ORDER BY name;"; var dtTable = Read(sqlCommand).Tables[0]; var tableDefinitionList = new List(); diff --git a/src/NzbDrone.Core/Datastore/TableMapping.cs b/src/NzbDrone.Core/Datastore/TableMapping.cs index 546d1288b..637f61b99 100644 --- a/src/NzbDrone.Core/Datastore/TableMapping.cs +++ b/src/NzbDrone.Core/Datastore/TableMapping.cs @@ -109,7 +109,6 @@ namespace NzbDrone.Core.Datastore SqlMapper.RemoveTypeMap(typeof(DateTime)); SqlMapper.AddTypeHandler(new DapperUtcConverter()); - SqlMapper.AddTypeHandler(new DapperTimeSpanConverter()); SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter>()); SqlMapper.AddTypeHandler(new CookieConverter()); SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter>()); @@ -123,6 +122,9 @@ namespace NzbDrone.Core.Datastore SqlMapper.RemoveTypeMap(typeof(Guid)); SqlMapper.RemoveTypeMap(typeof(Guid?)); SqlMapper.AddTypeHandler(new GuidConverter()); + SqlMapper.RemoveTypeMap(typeof(TimeSpan)); + SqlMapper.RemoveTypeMap(typeof(TimeSpan?)); + SqlMapper.AddTypeHandler(new TimeSpanConverter()); SqlMapper.AddTypeHandler(new CommandConverter()); SqlMapper.AddTypeHandler(new SystemVersionConverter()); } diff --git a/src/NzbDrone.Core/Download/Clients/Aria2/Aria2.cs b/src/NzbDrone.Core/Download/Clients/Aria2/Aria2.cs index ae076c14b..3085dbf63 100644 --- a/src/NzbDrone.Core/Download/Clients/Aria2/Aria2.cs +++ b/src/NzbDrone.Core/Download/Clients/Aria2/Aria2.cs @@ -7,6 +7,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Validation; @@ -25,8 +26,9 @@ namespace NzbDrone.Core.Download.Clients.Aria2 ISeedConfigProvider seedConfigProvider, IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger) + : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger) { _proxy = proxy; } diff --git a/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs b/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs index f8fd44e97..9e22a7460 100644 --- a/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs +++ b/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs @@ -8,6 +8,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Download.Clients.Blackhole @@ -20,8 +21,9 @@ namespace NzbDrone.Core.Download.Clients.Blackhole ISeedConfigProvider seedConfigProvider, IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger) + : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger) { } diff --git a/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs b/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs index 0f0364e61..0b64150ee 100644 --- a/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs +++ b/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs @@ -7,6 +7,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Download.Clients.Blackhole @@ -16,8 +17,9 @@ namespace NzbDrone.Core.Download.Clients.Blackhole public UsenetBlackhole(IHttpClient httpClient, IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(httpClient, configService, diskProvider, logger) + : base(httpClient, configService, diskProvider, localizationService, logger) { } diff --git a/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs b/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs index ca63ba0e7..90bd6ba1f 100644 --- a/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs +++ b/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs @@ -9,6 +9,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Validation; @@ -23,8 +24,9 @@ namespace NzbDrone.Core.Download.Clients.Deluge ISeedConfigProvider seedConfigProvider, IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger) + : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger) { _proxy = proxy; } diff --git a/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs b/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs index 482e86e30..97afe9480 100644 --- a/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs +++ b/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs @@ -10,6 +10,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Download.Clients.DownloadStation.Proxies; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; @@ -33,8 +34,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation ISeedConfigProvider seedConfigProvider, IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger) + : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger) { _dsInfoProxy = dsInfoProxy; _dsTaskProxySelector = dsTaskProxySelector; diff --git a/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs b/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs index 4661c6518..e78f5f5d2 100644 --- a/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs +++ b/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs @@ -9,6 +9,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Download.Clients.DownloadStation.Proxies; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; @@ -31,8 +32,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation IHttpClient httpClient, IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(httpClient, configService, diskProvider, logger) + : base(httpClient, configService, diskProvider, localizationService, logger) { _dsInfoProxy = dsInfoProxy; _dsTaskProxySelector = dsTaskProxySelector; diff --git a/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs b/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs index 845b4fa1c..5d39d7b5f 100644 --- a/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs +++ b/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs @@ -8,6 +8,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Download.Clients.Flood.Models; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.ThingiProvider; @@ -22,8 +23,9 @@ namespace NzbDrone.Core.Download.Clients.Flood ISeedConfigProvider seedConfigProvider, IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger) + : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger) { _proxy = proxy; } diff --git a/src/NzbDrone.Core/Download/Clients/FreeboxDownload/TorrentFreeboxDownload.cs b/src/NzbDrone.Core/Download/Clients/FreeboxDownload/TorrentFreeboxDownload.cs index cec808592..00e7e06b4 100644 --- a/src/NzbDrone.Core/Download/Clients/FreeboxDownload/TorrentFreeboxDownload.cs +++ b/src/NzbDrone.Core/Download/Clients/FreeboxDownload/TorrentFreeboxDownload.cs @@ -6,6 +6,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Download.Clients.FreeboxDownload @@ -19,8 +20,9 @@ namespace NzbDrone.Core.Download.Clients.FreeboxDownload ISeedConfigProvider seedConfigProvider, IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger) + : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger) { _proxy = proxy; } diff --git a/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs b/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs index b5aa1eb06..560c40eb3 100644 --- a/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs +++ b/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs @@ -6,6 +6,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Validation; @@ -20,8 +21,9 @@ namespace NzbDrone.Core.Download.Clients.Hadouken ISeedConfigProvider seedConfigProvider, IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger) + : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger) { _proxy = proxy; } diff --git a/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs b/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs index e1088780d..a87460d71 100644 --- a/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs +++ b/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs @@ -7,6 +7,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Validation; @@ -20,8 +21,9 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex IHttpClient httpClient, IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(httpClient, configService, diskProvider, logger) + : base(httpClient, configService, diskProvider, localizationService, logger) { _proxy = proxy; } diff --git a/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs b/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs index b286bf922..49ada68e6 100644 --- a/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs +++ b/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs @@ -10,6 +10,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Exceptions; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Validation; @@ -18,15 +19,14 @@ namespace NzbDrone.Core.Download.Clients.Nzbget public class Nzbget : UsenetClientBase { private readonly INzbgetProxy _proxy; - private readonly string[] _successStatus = { "SUCCESS", "NONE" }; - private readonly string[] _deleteFailedStatus = { "HEALTH", "DUPE", "SCAN", "COPY", "BAD" }; public Nzbget(INzbgetProxy proxy, IHttpClient httpClient, IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(httpClient, configService, diskProvider, logger) + : base(httpClient, configService, diskProvider, localizationService, logger) { _proxy = proxy; } diff --git a/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs b/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs index 1e98734f7..99f80ab22 100644 --- a/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs +++ b/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs @@ -8,6 +8,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; @@ -17,8 +18,9 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic { public Pneumatic(IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(configService, diskProvider, logger) + : base(configService, diskProvider, localizationService, logger) { } @@ -39,9 +41,9 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic _logger.Debug("Downloading NZB from: {0} to: {1}", url, nzbFile); - var nzbData = await indexer.Download(url); + var downloadResponse = await indexer.Download(url); - File.WriteAllBytes(nzbFile, nzbData); + await File.WriteAllBytesAsync(nzbFile, downloadResponse.Data); _logger.Debug("NZB Download succeeded, saved to: {0}", nzbFile); diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs index 6555ce6cc..3d1863784 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs @@ -8,6 +8,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Validation; @@ -30,8 +31,9 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent IConfigService configService, IDiskProvider diskProvider, ICacheManager cacheManager, + ILocalizationService localizationService, Logger logger) - : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger) + : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger) { _proxySelector = proxySelector; diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs index 14bee89fc..33fcfc5ca 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs @@ -25,8 +25,6 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent Dictionary GetLabels(QBittorrentSettings settings); void SetTorrentSeedingConfiguration(string hash, TorrentSeedConfiguration seedConfiguration, QBittorrentSettings settings); void MoveTorrentToTopInQueue(string hash, QBittorrentSettings settings); - void PauseTorrent(string hash, QBittorrentSettings settings); - void ResumeTorrent(string hash, QBittorrentSettings settings); void SetForceStart(string hash, bool enabled, QBittorrentSettings settings); } diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs index cdf161c7f..7c9cc8768 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs @@ -146,7 +146,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent { request.AddFormParameter("paused", false); } - else if ((QBittorrentState)settings.InitialState == QBittorrentState.Pause) + else if ((QBittorrentState)settings.InitialState == QBittorrentState.Stop) { request.AddFormParameter("paused", true); } @@ -176,7 +176,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent { request.AddFormParameter("paused", false); } - else if ((QBittorrentState)settings.InitialState == QBittorrentState.Pause) + else if ((QBittorrentState)settings.InitialState == QBittorrentState.Stop) { request.AddFormParameter("paused", true); } @@ -212,7 +212,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent catch (DownloadClientException ex) { // if setCategory fails due to method not being found, then try older setLabel command for qBittorrent < v.3.3.5 - if (ex.InnerException is HttpException && (ex.InnerException as HttpException).Response.StatusCode == HttpStatusCode.NotFound) + if (ex.InnerException is HttpException httpException && httpException.Response.StatusCode == HttpStatusCode.NotFound) { var setLabelRequest = BuildRequest(settings).Resource("/command/setLabel") .Post() @@ -254,7 +254,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent catch (DownloadClientException ex) { // qBittorrent rejects all Prio commands with 403: Forbidden if Options -> BitTorrent -> Torrent Queueing is not enabled - if (ex.InnerException is HttpException && (ex.InnerException as HttpException).Response.StatusCode == HttpStatusCode.Forbidden) + if (ex.InnerException is HttpException httpException && httpException.Response.StatusCode == HttpStatusCode.Forbidden) { return; } @@ -263,22 +263,6 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent } } - public void PauseTorrent(string hash, QBittorrentSettings settings) - { - var request = BuildRequest(settings).Resource("/command/pause") - .Post() - .AddFormParameter("hash", hash); - ProcessRequest(request, settings); - } - - public void ResumeTorrent(string hash, QBittorrentSettings settings) - { - var request = BuildRequest(settings).Resource("/command/resume") - .Post() - .AddFormParameter("hash", hash); - ProcessRequest(request, settings); - } - public void SetForceStart(string hash, bool enabled, QBittorrentSettings settings) { var request = BuildRequest(settings).Resource("/command/setForceStart") diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs index 0d9d55be1..cfa7c9934 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs @@ -246,14 +246,20 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent request.AddFormParameter("category", category); } - // Note: ForceStart is handled by separate api call - if ((QBittorrentState)settings.InitialState == QBittorrentState.Start) + // Avoid extraneous API version check if initial state is ForceStart + if ((QBittorrentState)settings.InitialState is QBittorrentState.Start or QBittorrentState.Stop) { - request.AddFormParameter("paused", false); - } - else if ((QBittorrentState)settings.InitialState == QBittorrentState.Pause) - { - request.AddFormParameter("paused", true); + var stoppedParameterName = GetApiVersion(settings) >= new Version(2, 11, 0) ? "stopped" : "paused"; + + // Note: ForceStart is handled by separate api call + if ((QBittorrentState)settings.InitialState == QBittorrentState.Start) + { + request.AddFormParameter(stoppedParameterName, false); + } + else if ((QBittorrentState)settings.InitialState == QBittorrentState.Stop) + { + request.AddFormParameter(stoppedParameterName, true); + } } if (settings.SequentialOrder) @@ -291,7 +297,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent catch (DownloadClientException ex) { // setShareLimits was added in api v2.0.1 so catch it case of the unlikely event that someone has api v2.0 - if (ex.InnerException is HttpException && (ex.InnerException as HttpException).Response.StatusCode == HttpStatusCode.NotFound) + if (ex.InnerException is HttpException httpException && httpException.Response.StatusCode == HttpStatusCode.NotFound) { return; } @@ -313,7 +319,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent catch (DownloadClientException ex) { // qBittorrent rejects all Prio commands with 409: Conflict if Options -> BitTorrent -> Torrent Queueing is not enabled - if (ex.InnerException is HttpException && (ex.InnerException as HttpException).Response.StatusCode == HttpStatusCode.Conflict) + if (ex.InnerException is HttpException httpException && httpException.Response.StatusCode == HttpStatusCode.Conflict) { return; } @@ -322,22 +328,6 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent } } - public void PauseTorrent(string hash, QBittorrentSettings settings) - { - var request = BuildRequest(settings).Resource("/api/v2/torrents/pause") - .Post() - .AddFormParameter("hashes", hash); - ProcessRequest(request, settings); - } - - public void ResumeTorrent(string hash, QBittorrentSettings settings) - { - var request = BuildRequest(settings).Resource("/api/v2/torrents/resume") - .Post() - .AddFormParameter("hashes", hash); - ProcessRequest(request, settings); - } - public void SetForceStart(string hash, bool enabled, QBittorrentSettings settings) { var request = BuildRequest(settings).Resource("/api/v2/torrents/setForceStart") diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentState.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentState.cs index 56c5ddf1a..b8fddbc11 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentState.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentState.cs @@ -1,9 +1,16 @@ +using NzbDrone.Core.Annotations; + namespace NzbDrone.Core.Download.Clients.QBittorrent { public enum QBittorrentState { + [FieldOption(Label = "Started")] Start = 0, + + [FieldOption(Label = "Force Started")] ForceStart = 1, - Pause = 2 + + [FieldOption(Label = "Stopped")] + Stop = 2 } } diff --git a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs index 584e392b2..246262527 100644 --- a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs +++ b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs @@ -9,6 +9,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Exceptions; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Validation; @@ -22,8 +23,9 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd IHttpClient httpClient, IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(httpClient, configService, diskProvider, logger) + : base(httpClient, configService, diskProvider, localizationService, logger) { _proxy = proxy; } diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/Transmission.cs b/src/NzbDrone.Core/Download/Clients/Transmission/Transmission.cs index c62a1f6ff..ad14de894 100644 --- a/src/NzbDrone.Core/Download/Clients/Transmission/Transmission.cs +++ b/src/NzbDrone.Core/Download/Clients/Transmission/Transmission.cs @@ -5,6 +5,7 @@ using NLog; using NzbDrone.Common.Disk; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; namespace NzbDrone.Core.Download.Clients.Transmission { @@ -15,8 +16,9 @@ namespace NzbDrone.Core.Download.Clients.Transmission ISeedConfigProvider seedConfigProvider, IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(proxy, torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger) + : base(proxy, torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger) { } diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs index f5cf1fed9..977e3bfee 100644 --- a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs +++ b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs @@ -6,6 +6,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Validation; @@ -20,8 +21,9 @@ namespace NzbDrone.Core.Download.Clients.Transmission ISeedConfigProvider seedConfigProvider, IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger) + : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger) { _proxy = proxy; } diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionProxy.cs b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionProxy.cs index 2c2334f44..76f8684e0 100644 --- a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionProxy.cs +++ b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionProxy.cs @@ -4,6 +4,7 @@ using System.Net; using Newtonsoft.Json.Linq; using NLog; using NzbDrone.Common.Cache; +using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Common.Serializer; @@ -208,7 +209,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission private void AuthenticateClient(HttpRequestBuilder requestBuilder, TransmissionSettings settings, bool reauthenticate = false) { - var authKey = string.Format("{0}:{1}", requestBuilder.BaseUrl, settings.Password); + var authKey = $"{requestBuilder.BaseUrl}:{settings.Password}"; var sessionId = _authSessionIDCache.Find(authKey); @@ -220,24 +221,26 @@ namespace NzbDrone.Core.Download.Clients.Transmission authLoginRequest.SuppressHttpError = true; var response = _httpClient.Execute(authLoginRequest); - if (response.StatusCode == HttpStatusCode.MovedPermanently) - { - var url = response.Headers.GetSingleValue("Location"); - throw new DownloadClientException("Remote site redirected to " + url); - } - else if (response.StatusCode == HttpStatusCode.Conflict) + switch (response.StatusCode) { - sessionId = response.Headers.GetSingleValue("X-Transmission-Session-Id"); + case HttpStatusCode.MovedPermanently: + var url = response.Headers.GetSingleValue("Location"); - if (sessionId == null) - { - throw new DownloadClientException("Remote host did not return a Session Id."); - } - } - else - { - throw new DownloadClientAuthenticationException("Failed to authenticate with Transmission."); + throw new DownloadClientException("Remote site redirected to " + url); + case HttpStatusCode.Forbidden: + throw new DownloadClientException($"Failed to authenticate with Transmission. It may be necessary to add {BuildInfo.AppName}'s IP address to RPC whitelist."); + case HttpStatusCode.Conflict: + sessionId = response.Headers.GetSingleValue("X-Transmission-Session-Id"); + + if (sessionId == null) + { + throw new DownloadClientException("Remote host did not return a Session Id."); + } + + break; + default: + throw new DownloadClientAuthenticationException("Failed to authenticate with Transmission."); } _logger.Debug("Transmission authentication succeeded."); diff --git a/src/NzbDrone.Core/Download/Clients/Vuze/Vuze.cs b/src/NzbDrone.Core/Download/Clients/Vuze/Vuze.cs index 79f5df0e4..3b87962bb 100644 --- a/src/NzbDrone.Core/Download/Clients/Vuze/Vuze.cs +++ b/src/NzbDrone.Core/Download/Clients/Vuze/Vuze.cs @@ -4,6 +4,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Core.Configuration; using NzbDrone.Core.Download.Clients.Transmission; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; namespace NzbDrone.Core.Download.Clients.Vuze { @@ -16,8 +17,9 @@ namespace NzbDrone.Core.Download.Clients.Vuze ISeedConfigProvider seedConfigProvider, IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(proxy, torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger) + : base(proxy, torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger) { } diff --git a/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs b/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs index 5218f9ebe..628ebdf52 100644 --- a/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs @@ -10,6 +10,7 @@ using NzbDrone.Core.Configuration; using NzbDrone.Core.Download.Clients.rTorrent; using NzbDrone.Core.Exceptions; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; @@ -27,8 +28,9 @@ namespace NzbDrone.Core.Download.Clients.RTorrent IConfigService configService, IDiskProvider diskProvider, IRTorrentDirectoryValidator rTorrentDirectoryValidator, + ILocalizationService localizationService, Logger logger) - : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger) + : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger) { _proxy = proxy; _rTorrentDirectoryValidator = rTorrentDirectoryValidator; diff --git a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs index ef12042fb..98ad41eec 100644 --- a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs @@ -7,7 +7,9 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; namespace NzbDrone.Core.Download.Clients.UTorrent @@ -21,8 +23,9 @@ namespace NzbDrone.Core.Download.Clients.UTorrent ISeedConfigProvider seedConfigProvider, IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger) + : base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger) { _proxy = proxy; } @@ -72,6 +75,9 @@ namespace NzbDrone.Core.Download.Clients.UTorrent } public override string Name => "uTorrent"; + + public override ProviderMessage Message => new (_localizationService.GetLocalizedString("DownloadClientUTorrentProviderMessage"), ProviderMessageType.Warning); + public override bool SupportsCategories => true; protected override void Test(List failures) diff --git a/src/NzbDrone.Core/Download/DownloadClientBase.cs b/src/NzbDrone.Core/Download/DownloadClientBase.cs index 7659c32a6..9a7da6fd6 100644 --- a/src/NzbDrone.Core/Download/DownloadClientBase.cs +++ b/src/NzbDrone.Core/Download/DownloadClientBase.cs @@ -8,6 +8,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; @@ -19,6 +20,7 @@ namespace NzbDrone.Core.Download { protected readonly IConfigService _configService; protected readonly IDiskProvider _diskProvider; + protected readonly ILocalizationService _localizationService; protected readonly Logger _logger; public abstract string Name { get; } @@ -40,10 +42,12 @@ namespace NzbDrone.Core.Download protected DownloadClientBase(IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) { _configService = configService; _diskProvider = diskProvider; + _localizationService = localizationService; _logger = logger; } diff --git a/src/NzbDrone.Core/Download/DownloadClientFactory.cs b/src/NzbDrone.Core/Download/DownloadClientFactory.cs index e7eacbbe3..19aedf751 100644 --- a/src/NzbDrone.Core/Download/DownloadClientFactory.cs +++ b/src/NzbDrone.Core/Download/DownloadClientFactory.cs @@ -61,7 +61,7 @@ namespace NzbDrone.Core.Download foreach (var client in clients) { - if (blockedClients.TryGetValue(client.Definition.Id, out var downloadClientStatus)) + if (blockedClients.TryGetValue(client.Definition.Id, out var downloadClientStatus) && downloadClientStatus.DisabledTill.HasValue) { _logger.Debug("Temporarily ignoring download client {0} till {1} due to recent failures.", client.Definition.Name, downloadClientStatus.DisabledTill.Value.ToLocalTime()); continue; diff --git a/src/NzbDrone.Core/Download/DownloadService.cs b/src/NzbDrone.Core/Download/DownloadService.cs index 675ab3b8e..223c24384 100644 --- a/src/NzbDrone.Core/Download/DownloadService.cs +++ b/src/NzbDrone.Core/Download/DownloadService.cs @@ -1,10 +1,8 @@ using System; using System.Threading.Tasks; using NLog; -using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Common.Instrumentation.Extensions; -using NzbDrone.Common.TPL; using NzbDrone.Core.Download.Clients; using NzbDrone.Core.Exceptions; using NzbDrone.Core.Indexers; @@ -27,7 +25,6 @@ namespace NzbDrone.Core.Download private readonly IDownloadClientStatusService _downloadClientStatusService; private readonly IIndexerFactory _indexerFactory; private readonly IIndexerStatusService _indexerStatusService; - private readonly IRateLimitService _rateLimitService; private readonly IEventAggregator _eventAggregator; private readonly Logger _logger; @@ -35,7 +32,6 @@ namespace NzbDrone.Core.Download IDownloadClientStatusService downloadClientStatusService, IIndexerFactory indexerFactory, IIndexerStatusService indexerStatusService, - IRateLimitService rateLimitService, IEventAggregator eventAggregator, Logger logger) { @@ -43,7 +39,6 @@ namespace NzbDrone.Core.Download _downloadClientStatusService = downloadClientStatusService; _indexerFactory = indexerFactory; _indexerStatusService = indexerStatusService; - _rateLimitService = rateLimitService; _eventAggregator = eventAggregator; _logger = logger; } @@ -132,15 +127,7 @@ namespace NzbDrone.Core.Download _logger.Trace("Attempting download of {0}", link); var url = new Uri(link); - // Limit grabs to 2 per second. - if (link.IsNotNullOrWhiteSpace() && !link.StartsWith("magnet:")) - { - await _rateLimitService.WaitAndPulseAsync(url.Host, TimeSpan.FromSeconds(2)); - } - var indexer = _indexerFactory.GetInstance(_indexerFactory.Get(indexerId)); - var success = false; - var downloadedBytes = Array.Empty(); var release = new ReleaseInfo { @@ -151,17 +138,21 @@ namespace NzbDrone.Core.Download DownloadProtocol = indexer.Protocol }; - var grabEvent = new IndexerDownloadEvent(release, success, source, host, release.Title, release.DownloadUrl) + var grabEvent = new IndexerDownloadEvent(release, false, source, host, release.Title, release.DownloadUrl) { Indexer = indexer, GrabTrigger = source == "Prowlarr" ? GrabTrigger.Manual : GrabTrigger.Api }; + byte[] downloadedBytes; + try { - downloadedBytes = await indexer.Download(url); + var downloadResponse = await indexer.Download(url); + downloadedBytes = downloadResponse.Data; _indexerStatusService.RecordSuccess(indexerId); grabEvent.Successful = true; + grabEvent.ElapsedTime = downloadResponse.ElapsedTime; } catch (ReleaseUnavailableException) { diff --git a/src/NzbDrone.Core/Download/TorrentClientBase.cs b/src/NzbDrone.Core/Download/TorrentClientBase.cs index 26db20b44..217e70a31 100644 --- a/src/NzbDrone.Core/Download/TorrentClientBase.cs +++ b/src/NzbDrone.Core/Download/TorrentClientBase.cs @@ -8,6 +8,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Exceptions; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.ThingiProvider; @@ -24,8 +25,9 @@ namespace NzbDrone.Core.Download ISeedConfigProvider seedConfigProvider, IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(configService, diskProvider, logger) + : base(configService, diskProvider, localizationService, logger) { _torrentFileInfoReader = torrentFileInfoReader; _seedConfigProvider = seedConfigProvider; @@ -127,9 +129,8 @@ namespace NzbDrone.Core.Download private async Task DownloadFromWebUrl(TorrentInfo release, IIndexer indexer, string torrentUrl) { - byte[] torrentFile = null; - - torrentFile = await indexer.Download(new Uri(torrentUrl)); + var downloadResponse = await indexer.Download(new Uri(torrentUrl)); + var torrentFile = downloadResponse.Data; // handle magnet URLs if (torrentFile.Length >= 7 diff --git a/src/NzbDrone.Core/Download/UsenetClientBase.cs b/src/NzbDrone.Core/Download/UsenetClientBase.cs index 2541c6059..1e85ffbb9 100644 --- a/src/NzbDrone.Core/Download/UsenetClientBase.cs +++ b/src/NzbDrone.Core/Download/UsenetClientBase.cs @@ -5,6 +5,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.ThingiProvider; @@ -19,8 +20,9 @@ namespace NzbDrone.Core.Download protected UsenetClientBase(IHttpClient httpClient, IConfigService configService, IDiskProvider diskProvider, + ILocalizationService localizationService, Logger logger) - : base(configService, diskProvider, logger) + : base(configService, diskProvider, localizationService, logger) { _httpClient = httpClient; } @@ -41,12 +43,10 @@ namespace NzbDrone.Core.Download var filename = StringUtil.CleanFileName(release.Title) + ".nzb"; - byte[] nzbData; - - nzbData = await indexer.Download(url); + var downloadResponse = await indexer.Download(url); _logger.Info("Adding report [{0}] to the queue.", release.Title); - return AddFromNzbFile(release, filename, nzbData); + return AddFromNzbFile(release, filename, downloadResponse.Data); } } } diff --git a/src/NzbDrone.Core/Fluent.cs b/src/NzbDrone.Core/Fluent.cs index 37a775e96..483f52b6b 100644 --- a/src/NzbDrone.Core/Fluent.cs +++ b/src/NzbDrone.Core/Fluent.cs @@ -20,26 +20,6 @@ namespace NzbDrone.Core return actual; } - public static long Megabytes(this int megabytes) - { - return Convert.ToInt64(megabytes * 1024L * 1024L); - } - - public static long Gigabytes(this int gigabytes) - { - return Convert.ToInt64(gigabytes * 1024L * 1024L * 1024L); - } - - public static long Megabytes(this double megabytes) - { - return Convert.ToInt64(megabytes * 1024L * 1024L); - } - - public static long Gigabytes(this double gigabytes) - { - return Convert.ToInt64(gigabytes * 1024L * 1024L * 1024L); - } - public static long Round(this long number, long level) { return Convert.ToInt64(Math.Floor((decimal)number / level) * level); diff --git a/src/NzbDrone.Core/HealthCheck/Checks/NoDefinitionCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/IndexerNoDefinitionCheck.cs similarity index 53% rename from src/NzbDrone.Core/HealthCheck/Checks/NoDefinitionCheck.cs rename to src/NzbDrone.Core/HealthCheck/Checks/IndexerNoDefinitionCheck.cs index 73d217c25..d6f0ad90c 100644 --- a/src/NzbDrone.Core/HealthCheck/Checks/NoDefinitionCheck.cs +++ b/src/NzbDrone.Core/HealthCheck/Checks/IndexerNoDefinitionCheck.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Linq; using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers.Definitions.Cardigann; @@ -9,12 +10,12 @@ namespace NzbDrone.Core.HealthCheck.Checks { [CheckOn(typeof(ProviderDeletedEvent))] [CheckOn(typeof(ProviderBulkDeletedEvent))] - public class NoDefinitionCheck : HealthCheckBase + public class IndexerNoDefinitionCheck : HealthCheckBase { private readonly IIndexerDefinitionUpdateService _indexerDefinitionUpdateService; private readonly IIndexerFactory _indexerFactory; - public NoDefinitionCheck(IIndexerDefinitionUpdateService indexerDefinitionUpdateService, IIndexerFactory indexerFactory, ILocalizationService localizationService) + public IndexerNoDefinitionCheck(IIndexerDefinitionUpdateService indexerDefinitionUpdateService, IIndexerFactory indexerFactory, ILocalizationService localizationService) : base(localizationService) { _indexerDefinitionUpdateService = indexerDefinitionUpdateService; @@ -23,23 +24,22 @@ namespace NzbDrone.Core.HealthCheck.Checks public override HealthCheck Check() { - var currentDefs = _indexerDefinitionUpdateService.All(); + var currentDefinitions = _indexerDefinitionUpdateService.All(); + var noDefinitionIndexers = _indexerFactory.AllProviders(false) + .Where(i => i.Definition.Implementation == "Cardigann" && currentDefinitions.All(d => d.File != ((CardigannSettings)i.Definition.Settings).DefinitionFile)) + .ToList(); - var noDefIndexers = _indexerFactory.AllProviders(false) - .Where(i => i.Definition.Implementation == "Cardigann" && currentDefs.All(d => d.File != ((CardigannSettings)i.Definition.Settings).DefinitionFile)).ToList(); - - if (noDefIndexers.Count == 0) + if (noDefinitionIndexers.Count == 0) { return new HealthCheck(GetType()); } - var healthType = HealthCheckResult.Error; - var healthMessage = string.Format(_localizationService.GetLocalizedString("IndexerNoDefCheckMessage"), - string.Join(", ", noDefIndexers.Select(v => v.Definition.Name))); - return new HealthCheck(GetType(), - healthType, - healthMessage, + HealthCheckResult.Error, + _localizationService.GetLocalizedString("IndexerNoDefinitionCheckHealthCheckMessage", new Dictionary + { + { "indexerNames", string.Join(", ", noDefinitionIndexers.Select(v => v.Definition.Name).ToArray()) } + }), "#indexers-have-no-definition"); } diff --git a/src/NzbDrone.Core/HealthCheck/Checks/SystemTimeCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/SystemTimeCheck.cs index 8ee1f5a30..d2b3acf13 100644 --- a/src/NzbDrone.Core/HealthCheck/Checks/SystemTimeCheck.cs +++ b/src/NzbDrone.Core/HealthCheck/Checks/SystemTimeCheck.cs @@ -14,7 +14,7 @@ namespace NzbDrone.Core.HealthCheck.Checks private readonly IHttpRequestBuilderFactory _cloudRequestBuilder; private readonly Logger _logger; - public SystemTimeCheck(IHttpClient client, IProwlarrCloudRequestBuilder cloudRequestBuilder, ILocalizationService localizationService, Logger logger) + public SystemTimeCheck(IHttpClient client, IProwlarrCloudRequestBuilder cloudRequestBuilder, Logger logger, ILocalizationService localizationService) : base(localizationService) { _client = client; @@ -29,19 +29,26 @@ namespace NzbDrone.Core.HealthCheck.Checks return new HealthCheck(GetType()); } - var request = _cloudRequestBuilder.Create() - .Resource("/time") - .Build(); - - var response = _client.Execute(request); - var result = Json.Deserialize(response.Content); - var systemTime = DateTime.UtcNow; - - // +/- more than 1 day - if (Math.Abs(result.DateTimeUtc.Subtract(systemTime).TotalDays) >= 1) + try { - _logger.Error("System time mismatch. SystemTime: {0} Expected Time: {1}. Update system time", systemTime, result.DateTimeUtc); - return new HealthCheck(GetType(), HealthCheckResult.Error, _localizationService.GetLocalizedString("SystemTimeCheckMessage")); + var request = _cloudRequestBuilder.Create() + .Resource("/time") + .Build(); + + var response = _client.Execute(request); + var result = Json.Deserialize(response.Content); + var systemTime = DateTime.UtcNow; + + // +/- more than 1 day + if (Math.Abs(result.DateTimeUtc.Subtract(systemTime).TotalDays) >= 1) + { + _logger.Error("System time mismatch. SystemTime: {0} Expected Time: {1}. Update system time", systemTime, result.DateTimeUtc); + return new HealthCheck(GetType(), HealthCheckResult.Error, _localizationService.GetLocalizedString("SystemTimeHealthCheckMessage"), "#system-time-off"); + } + } + catch (Exception e) + { + _logger.Warn(e, "Unable to verify system time"); } return new HealthCheck(GetType()); diff --git a/src/NzbDrone.Core/HealthCheck/Checks/UpdateCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/UpdateCheck.cs index 09b3eea1f..684d7f60a 100644 --- a/src/NzbDrone.Core/HealthCheck/Checks/UpdateCheck.cs +++ b/src/NzbDrone.Core/HealthCheck/Checks/UpdateCheck.cs @@ -40,7 +40,7 @@ namespace NzbDrone.Core.HealthCheck.Checks var startupFolder = _appFolderInfo.StartUpFolder; var uiFolder = Path.Combine(startupFolder, "UI"); - if ((OsInfo.IsWindows || _configFileProvider.UpdateAutomatically) && + if (_configFileProvider.UpdateAutomatically && _configFileProvider.UpdateMechanism == UpdateMechanism.BuiltIn && !_osInfo.IsDocker) { @@ -86,9 +86,20 @@ namespace NzbDrone.Core.HealthCheck.Checks } } - if (BuildInfo.BuildDateTime < DateTime.UtcNow.AddDays(-14) && _checkUpdateService.AvailableUpdate() != null) + if (BuildInfo.BuildDateTime < DateTime.UtcNow.AddDays(-14)) { - return new HealthCheck(GetType(), HealthCheckResult.Warning, _localizationService.GetLocalizedString("UpdateAvailableHealthCheckMessage")); + var latestAvailable = _checkUpdateService.AvailableUpdate(); + + if (latestAvailable != null) + { + return new HealthCheck(GetType(), + BuildInfo.BuildDateTime.Before(DateTime.UtcNow.AddDays(-180)) ? HealthCheckResult.Error : HealthCheckResult.Warning, + _localizationService.GetLocalizedString("UpdateAvailableHealthCheckMessage", new Dictionary + { + { "version", $"v{latestAvailable.Version}" } + }), + "#new-update-is-available"); + } } return new HealthCheck(GetType()); diff --git a/src/NzbDrone.Core/History/HistoryService.cs b/src/NzbDrone.Core/History/HistoryService.cs index a5ee00372..e78e5d229 100644 --- a/src/NzbDrone.Core/History/HistoryService.cs +++ b/src/NzbDrone.Core/History/HistoryService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Net; using NLog; +using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Datastore; using NzbDrone.Core.Indexers; @@ -203,9 +204,29 @@ namespace NzbDrone.Core.History history.Data.Add("Host", message.Host ?? string.Empty); history.Data.Add("GrabMethod", message.Redirect ? "Redirect" : "Proxy"); history.Data.Add("GrabTitle", message.Title); - history.Data.Add("Categories", string.Join(",", message.Release.Categories.Select(x => x.Id) ?? Array.Empty())); history.Data.Add("Url", message.Url ?? string.Empty); + if (message.ElapsedTime > 0) + { + history.Data.Add("ElapsedTime", message.ElapsedTime.ToString()); + } + + if (message.Release.InfoUrl.IsNotNullOrWhiteSpace()) + { + history.Data.Add("InfoUrl", message.Release.InfoUrl); + } + + if (message.DownloadClient.IsNotNullOrWhiteSpace() || message.DownloadClientName.IsNotNullOrWhiteSpace()) + { + history.Data.Add("DownloadClient", message.DownloadClient ?? string.Empty); + history.Data.Add("DownloadClientName", message.DownloadClientName ?? string.Empty); + } + + if (message.Release.PublishDate != DateTime.MinValue) + { + history.Data.Add("PublishedDate", message.Release.PublishDate.ToUniversalTime().ToString("s") + "Z"); + } + _historyRepository.Insert(history); } @@ -219,7 +240,7 @@ namespace NzbDrone.Core.History Successful = message.Successful }; - history.Data.Add("ElapsedTime", message.Time.ToString()); + history.Data.Add("ElapsedTime", message.ElapsedTime.ToString()); _historyRepository.Insert(history); } diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/TrimLogDatabase.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/TrimLogDatabase.cs index a719652af..5763a563e 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/TrimLogDatabase.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/TrimLogDatabase.cs @@ -1,18 +1,26 @@ -using NzbDrone.Core.Instrumentation; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Instrumentation; namespace NzbDrone.Core.Housekeeping.Housekeepers { public class TrimLogDatabase : IHousekeepingTask { private readonly ILogRepository _logRepo; + private readonly IConfigFileProvider _configFileProvider; - public TrimLogDatabase(ILogRepository logRepo) + public TrimLogDatabase(ILogRepository logRepo, IConfigFileProvider configFileProvider) { _logRepo = logRepo; + _configFileProvider = configFileProvider; } public void Clean() { + if (!_configFileProvider.LogDbEnabled) + { + return; + } + _logRepo.Trim(); } } diff --git a/src/NzbDrone.Core/Http/HttpProxySettingsProvider.cs b/src/NzbDrone.Core/Http/HttpProxySettingsProvider.cs index a56bcf2e6..24b5aa67f 100644 --- a/src/NzbDrone.Core/Http/HttpProxySettingsProvider.cs +++ b/src/NzbDrone.Core/Http/HttpProxySettingsProvider.cs @@ -1,5 +1,7 @@ using System; +using System.Linq; using System.Net; +using NetTools; using NzbDrone.Common.Http; using NzbDrone.Common.Http.Proxy; using NzbDrone.Core.Configuration; @@ -52,7 +54,15 @@ namespace NzbDrone.Core.Http //We are utilizing the WebProxy implementation here to save us having to re-implement it. This way we use Microsofts implementation var proxy = new WebProxy(proxySettings.Host + ":" + proxySettings.Port, proxySettings.BypassLocalAddress, proxySettings.BypassListAsArray); - return proxy.IsBypassed((Uri)url); + return proxy.IsBypassed((Uri)url) || IsBypassedByIpAddressRange(proxySettings.BypassListAsArray, url.Host); + } + + private static bool IsBypassedByIpAddressRange(string[] bypassList, string host) + { + return bypassList.Any(bypass => + IPAddressRange.TryParse(bypass, out var ipAddressRange) && + IPAddress.TryParse(host, out var ipAddress) && + ipAddressRange.Contains(ipAddress)); } } } diff --git a/src/NzbDrone.Core/IndexerSearch/Definitions/TvSearchCriteria.cs b/src/NzbDrone.Core/IndexerSearch/Definitions/TvSearchCriteria.cs index a79878a98..d4c13c863 100644 --- a/src/NzbDrone.Core/IndexerSearch/Definitions/TvSearchCriteria.cs +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/TvSearchCriteria.cs @@ -31,9 +31,7 @@ namespace NzbDrone.Core.IndexerSearch.Definitions !IsIdSearch; public override bool IsIdSearch => - Episode.IsNotNullOrWhiteSpace() || ImdbId.IsNotNullOrWhiteSpace() || - Season.HasValue || TvdbId.HasValue || RId.HasValue || TraktId.HasValue || @@ -116,7 +114,7 @@ namespace NzbDrone.Core.IndexerSearch.Definitions string episodeString; if (DateTime.TryParseExact($"{Season} {Episode}", "yyyy MM/dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var showDate)) { - episodeString = showDate.ToString("yyyy.MM.dd"); + episodeString = showDate.ToString("yyyy.MM.dd", CultureInfo.InvariantCulture); } else if (Episode.IsNullOrWhiteSpace()) { diff --git a/src/NzbDrone.Core/IndexerSearch/NewznabResults.cs b/src/NzbDrone.Core/IndexerSearch/NewznabResults.cs index e41d758d4..6b398da77 100644 --- a/src/NzbDrone.Core/IndexerSearch/NewznabResults.cs +++ b/src/NzbDrone.Core/IndexerSearch/NewznabResults.cs @@ -54,7 +54,7 @@ namespace NzbDrone.Core.IndexerSearch return new XElement(feedNamespace + "attr", new XAttribute("name", name), new XAttribute("value", value)); } - public string ToXml(DownloadProtocol protocol) + public string ToXml(DownloadProtocol protocol, bool preferMagnetUrl = false) { // IMPORTANT: We can't use Uri.ToString(), because it generates URLs without URL encode (links with unicode // characters are broken). We must use Uri.AbsoluteUri instead that handles encoding correctly @@ -73,6 +73,7 @@ namespace NzbDrone.Core.IndexerSearch new XElement("title", "Prowlarr"), from r in Releases let t = (r as TorrentInfo) ?? new TorrentInfo() + let downloadUrl = preferMagnetUrl ? t.MagnetUrl ?? r.DownloadUrl : r.DownloadUrl ?? t.MagnetUrl select new XElement("item", new XElement("title", RemoveInvalidXMLChars(r.Title)), new XElement("description", RemoveInvalidXMLChars(r.Description)), @@ -85,11 +86,11 @@ namespace NzbDrone.Core.IndexerSearch r.InfoUrl == null ? null : new XElement("comments", r.InfoUrl), r.PublishDate == DateTime.MinValue ? new XElement("pubDate", XmlDateFormat(DateTime.Now)) : new XElement("pubDate", XmlDateFormat(r.PublishDate)), new XElement("size", r.Size), - new XElement("link", r.DownloadUrl ?? t.MagnetUrl ?? string.Empty), + new XElement("link", downloadUrl ?? string.Empty), r.Categories == null ? null : from c in r.Categories select new XElement("category", c.Id), new XElement( "enclosure", - new XAttribute("url", r.DownloadUrl ?? t.MagnetUrl ?? string.Empty), + new XAttribute("url", downloadUrl ?? string.Empty), r.Size == null ? null : new XAttribute("length", r.Size), new XAttribute("type", protocol == DownloadProtocol.Torrent ? "application/x-bittorrent" : "application/x-nzb")), r.Categories == null ? null : from c in r.Categories select GetNabElement("category", c.Id, protocol), diff --git a/src/NzbDrone.Core/IndexerStats/IndexerStatistics.cs b/src/NzbDrone.Core/IndexerStats/IndexerStatistics.cs index 261d0052a..76cbfd616 100644 --- a/src/NzbDrone.Core/IndexerStats/IndexerStatistics.cs +++ b/src/NzbDrone.Core/IndexerStats/IndexerStatistics.cs @@ -15,6 +15,7 @@ namespace NzbDrone.Core.IndexerStats public int IndexerId { get; set; } public string IndexerName { get; set; } public int AverageResponseTime { get; set; } + public int AverageGrabResponseTime { get; set; } public int NumberOfQueries { get; set; } public int NumberOfGrabs { get; set; } public int NumberOfRssQueries { get; set; } diff --git a/src/NzbDrone.Core/IndexerStats/IndexerStatisticsService.cs b/src/NzbDrone.Core/IndexerStats/IndexerStatisticsService.cs index fd603ece8..bfaa6e508 100644 --- a/src/NzbDrone.Core/IndexerStats/IndexerStatisticsService.cs +++ b/src/NzbDrone.Core/IndexerStats/IndexerStatisticsService.cs @@ -26,11 +26,15 @@ namespace NzbDrone.Core.IndexerStats { var history = _historyService.Between(start, end); - var filteredHistory = history.Where(h => indexerIds.Contains(h.IndexerId)); + var filteredHistory = history.Where(h => indexerIds.Contains(h.IndexerId)).ToArray(); - var groupedByIndexer = filteredHistory.GroupBy(h => h.IndexerId); - var groupedByUserAgent = filteredHistory.GroupBy(h => h.Data.GetValueOrDefault("source") ?? ""); - var groupedByHost = filteredHistory.GroupBy(h => h.Data.GetValueOrDefault("host") ?? ""); + var groupedByIndexer = filteredHistory.GroupBy(h => h.IndexerId).ToArray(); + var groupedByUserAgent = filteredHistory + .Where(h => h.EventType != HistoryEventType.IndexerAuth) + .GroupBy(h => h.Data.GetValueOrDefault("source") ?? "").ToArray(); + var groupedByHost = filteredHistory + .Where(h => h.EventType != HistoryEventType.IndexerAuth) + .GroupBy(h => h.Data.GetValueOrDefault("host") ?? "").ToArray(); var indexerStatsList = new List(); var userAgentStatsList = new List(); @@ -57,17 +61,13 @@ namespace NzbDrone.Core.IndexerStats .ThenBy(v => v.Id) .ToArray(); - var temp = 0; - var elapsedTimeEvents = sortedEvents - .Where(h => int.TryParse(h.Data.GetValueOrDefault("elapsedTime"), out temp) && h.Data.GetValueOrDefault("cached") != "1") - .Select(h => temp) - .ToArray(); - - indexerStats.AverageResponseTime = elapsedTimeEvents.Any() ? (int)elapsedTimeEvents.Average() : 0; + indexerStats.AverageResponseTime = CalculateAverageElapsedTime(sortedEvents.Where(h => h.EventType is HistoryEventType.IndexerRss or HistoryEventType.IndexerQuery).ToArray()); + indexerStats.AverageGrabResponseTime = CalculateAverageElapsedTime(sortedEvents.Where(h => h.EventType is HistoryEventType.ReleaseGrabbed).ToArray()); foreach (var historyEvent in sortedEvents) { var failed = !historyEvent.Successful; + switch (historyEvent.EventType) { case HistoryEventType.IndexerQuery: @@ -101,8 +101,6 @@ namespace NzbDrone.Core.IndexerStats indexerStats.NumberOfFailedRssQueries++; } - break; - default: break; } } @@ -118,8 +116,8 @@ namespace NzbDrone.Core.IndexerStats }; var sortedEvents = indexer.OrderBy(v => v.Date) - .ThenBy(v => v.Id) - .ToArray(); + .ThenBy(v => v.Id) + .ToArray(); foreach (var historyEvent in sortedEvents) { @@ -128,13 +126,10 @@ namespace NzbDrone.Core.IndexerStats case HistoryEventType.IndexerRss: case HistoryEventType.IndexerQuery: indexerStats.NumberOfQueries++; - break; case HistoryEventType.ReleaseGrabbed: indexerStats.NumberOfGrabs++; break; - default: - break; } } @@ -149,8 +144,8 @@ namespace NzbDrone.Core.IndexerStats }; var sortedEvents = indexer.OrderBy(v => v.Date) - .ThenBy(v => v.Id) - .ToArray(); + .ThenBy(v => v.Id) + .ToArray(); foreach (var historyEvent in sortedEvents) { @@ -163,8 +158,6 @@ namespace NzbDrone.Core.IndexerStats case HistoryEventType.ReleaseGrabbed: indexerStats.NumberOfGrabs++; break; - default: - break; } } @@ -178,5 +171,17 @@ namespace NzbDrone.Core.IndexerStats HostStatistics = hostStatsList }; } + + private static int CalculateAverageElapsedTime(History.History[] sortedEvents) + { + var temp = 0; + + var elapsedTimeEvents = sortedEvents + .Where(h => int.TryParse(h.Data.GetValueOrDefault("elapsedTime"), out temp) && temp > 0 && h.Data.GetValueOrDefault("cached") != "1") + .Select(_ => temp) + .ToArray(); + + return elapsedTimeEvents.Any() ? (int)elapsedTimeEvents.Average() : 0; + } } } diff --git a/src/NzbDrone.Core/IndexerVersions/IndexerDefinitionUpdateService.cs b/src/NzbDrone.Core/IndexerVersions/IndexerDefinitionUpdateService.cs index 70c15fb10..bf886d02f 100644 --- a/src/NzbDrone.Core/IndexerVersions/IndexerDefinitionUpdateService.cs +++ b/src/NzbDrone.Core/IndexerVersions/IndexerDefinitionUpdateService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; +using System.Net; using NLog; using NzbDrone.Common.Cache; using NzbDrone.Common.Disk; @@ -29,7 +30,7 @@ namespace NzbDrone.Core.IndexerVersions /* Update Service will fall back if version # does not exist for an indexer per Ta */ private const string DEFINITION_BRANCH = "master"; - private const int DEFINITION_VERSION = 9; + private const int DEFINITION_VERSION = 11; // Used when moving yml to C# private readonly List _definitionBlocklist = new () @@ -94,8 +95,10 @@ namespace NzbDrone.Core.IndexerVersions var response = _httpClient.Get>(request); indexerList = response.Resource.Where(i => !_definitionBlocklist.Contains(i.File)).ToList(); } - catch + catch (Exception ex) { + _logger.Warn(ex, "Error while getting indexer definitions, fallback to reading from disk."); + var definitionFolder = Path.Combine(_appFolderInfo.AppDataFolder, "Definitions"); indexerList = ReadDefinitionsFromDisk(indexerList, definitionFolder); @@ -106,9 +109,9 @@ namespace NzbDrone.Core.IndexerVersions indexerList = ReadDefinitionsFromDisk(indexerList, customDefinitionFolder); } - catch + catch (Exception ex) { - _logger.Error("Failed to Connect to Indexer Definition Server for Indexer listing"); + _logger.Error(ex, "Failed to Connect to Indexer Definition Server for Indexer listing"); } return indexerList; @@ -116,7 +119,7 @@ namespace NzbDrone.Core.IndexerVersions public CardigannDefinition GetCachedDefinition(string fileKey) { - if (string.IsNullOrEmpty(fileKey)) + if (string.IsNullOrWhiteSpace(fileKey)) { throw new ArgumentNullException(nameof(fileKey)); } @@ -172,7 +175,7 @@ namespace NzbDrone.Core.IndexerVersions private CardigannDefinition GetUncachedDefinition(string fileKey) { - if (string.IsNullOrEmpty(fileKey)) + if (string.IsNullOrWhiteSpace(fileKey)) { throw new ArgumentNullException(nameof(fileKey)); } @@ -220,9 +223,24 @@ namespace NzbDrone.Core.IndexerVersions private CardigannDefinition GetHttpDefinition(string id) { - var request = new HttpRequest($"https://indexers.prowlarr.com/{DEFINITION_BRANCH}/{DEFINITION_VERSION}/{id}"); - var response = _httpClient.Get(request); - var definition = _deserializer.Deserialize(response.Content); + if (string.IsNullOrWhiteSpace(id)) + { + throw new ArgumentNullException(nameof(id)); + } + + CardigannDefinition definition; + + try + { + var request = new HttpRequest($"https://indexers.prowlarr.com/{DEFINITION_BRANCH}/{DEFINITION_VERSION}/{id}"); + var response = _httpClient.Get(request); + + definition = _deserializer.Deserialize(response.Content); + } + catch (HttpException ex) when (ex.Response.StatusCode == HttpStatusCode.NotFound) + { + throw new Exception($"Indexer definition for '{id}' does not exist.", ex); + } return CleanIndexerDefinition(definition); } diff --git a/src/NzbDrone.Core/Indexers/Definitions/AlphaRatio.cs b/src/NzbDrone.Core/Indexers/Definitions/AlphaRatio.cs index d40710da9..c11d57b57 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/AlphaRatio.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/AlphaRatio.cs @@ -143,7 +143,7 @@ public class AlphaRatioParser : GazelleParser .AddQueryParam("action", "download") .AddQueryParam("id", torrentId); - if (Settings.UseFreeleechToken && canUseToken) + if (Settings.UseFreeleechToken is (int)GazelleFreeleechTokenAction.Preferred or (int)GazelleFreeleechTokenAction.Required && canUseToken) { url = url.AddQueryParam("usetoken", "1"); } diff --git a/src/NzbDrone.Core/Indexers/Definitions/AnimeBytes.cs b/src/NzbDrone.Core/Indexers/Definitions/AnimeBytes.cs index b87d12403..f45ff4173 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/AnimeBytes.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/AnimeBytes.cs @@ -93,7 +93,7 @@ namespace NzbDrone.Core.Indexers.Definitions if (searchCriteria.IsRssSearch) { - cleanReleases = cleanReleases.Where(r => r.PublishDate > DateTime.Now.AddDays(-1)).ToList(); + cleanReleases = cleanReleases.Where((r, index) => r.PublishDate > DateTime.UtcNow.AddDays(-1) || index < 20).ToList(); } return cleanReleases.Select(r => (ReleaseInfo)r.Clone()).ToList(); @@ -227,7 +227,13 @@ namespace NzbDrone.Core.Indexers.Definitions } } - var queryCats = _capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories); + var queryCats = _capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories).Distinct().ToList(); + + if (queryCats.Any() && searchCriteria is TvSearchCriteria { Season: > 0 }) + { + // Avoid searching for specials if it's a non-zero season search + queryCats.RemoveAll(cat => cat is "anime[tv_special]" or "anime[ova]" or "anime[dvd_special]" or "anime[bd_special]"); + } if (queryCats.Any()) { @@ -246,9 +252,7 @@ namespace NzbDrone.Core.Indexers.Definitions searchUrl += "?" + parameters.GetQueryString(); - var request = new IndexerRequest(searchUrl, HttpAccept.Json); - - yield return request; + yield return new IndexerRequest(searchUrl, HttpAccept.Json); } private static string CleanSearchTerm(string term) @@ -297,6 +301,8 @@ namespace NzbDrone.Core.Indexers.Definitions }; private static readonly HashSet ExcludedFileExtensions = new (StringComparer.OrdinalIgnoreCase) { ".mka", ".mds", ".md5", ".nfo", ".sfv", ".ass", ".mks", ".srt", ".ssa", ".sup", ".jpeg", ".jpg", ".png", ".otf", ".ttf" }; + private static readonly string[] PropertiesSeparator = { " | ", " / " }; + private readonly AnimeBytesSettings _settings; public AnimeBytesParser(AnimeBytesSettings settings) @@ -320,6 +326,11 @@ namespace NzbDrone.Core.Indexers.Definitions var response = STJson.Deserialize(indexerResponse.Content); + if (response.Error.IsNotNullOrWhiteSpace()) + { + throw new IndexerException(indexerResponse, "Unexpected response from indexer request: {0}", response.Error); + } + if (response.Matches == 0) { return releaseInfos.ToArray(); @@ -339,7 +350,7 @@ namespace NzbDrone.Core.Indexers.Definitions mainTitle = seriesName; } - var synonyms = new HashSet + var synonyms = new HashSet(StringComparer.OrdinalIgnoreCase) { mainTitle }; @@ -389,38 +400,48 @@ namespace NzbDrone.Core.Indexers.Definitions var minimumSeedTime = 259200 + (int)(size / (int)Math.Pow(1024, 3) * 18000); var propertyList = WebUtility.HtmlDecode(torrent.Property) - .Split(new[] { " | ", " / " }, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries) + .Split(PropertiesSeparator, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries) .ToList(); propertyList.RemoveAll(p => ExcludedProperties.Any(p.ContainsIgnoreCase)); var properties = propertyList.ToHashSet(); - if (torrent.Files.Any(f => f.FileName.ContainsIgnoreCase("Remux"))) - { - var resolutionProperty = properties.FirstOrDefault(RemuxResolutions.ContainsIgnoreCase); - - if (resolutionProperty.IsNotNullOrWhiteSpace()) - { - properties.Add($"{resolutionProperty} Remux"); - } - } - - if (properties.Any(p => p.StartsWithIgnoreCase("M2TS"))) + if (properties.Any(p => p.StartsWith("M2TS", StringComparison.Ordinal))) { properties.Add("BR-DISK"); } - if (_settings.ExcludeRaw && - properties.Any(p => p.StartsWithIgnoreCase("RAW") || p.Contains("BR-DISK"))) + var isBluRayDisk = properties.Any(p => p.Equals("RAW", StringComparison.Ordinal) || p.StartsWith("M2TS", StringComparison.Ordinal) || p.StartsWith("ISO", StringComparison.Ordinal)); + + if (_settings.ExcludeRaw && isBluRayDisk) { continue; } + properties = properties + .Select(property => + { + if (isBluRayDisk) + { + property = Regex.Replace(property, @"\b(H\.?265)\b", "HEVC", RegexOptions.Compiled | RegexOptions.IgnoreCase); + property = Regex.Replace(property, @"\b(H\.?264)\b", "AVC", RegexOptions.Compiled | RegexOptions.IgnoreCase); + } + + if (torrent.Files.Any(f => f.FileName.ContainsIgnoreCase("Remux")) + && RemuxResolutions.ContainsIgnoreCase(property)) + { + property += " Remux"; + } + + return property; + }) + .ToHashSet(); + int? season = null; int? episode = null; var releaseInfo = _settings.EnableSonarrCompatibility && categoryName == "Anime" ? "S01" : ""; - var editionTitle = torrent.EditionData.EditionTitle; + var editionTitle = torrent.EditionData?.EditionTitle; if (editionTitle.IsNotNullOrWhiteSpace()) { @@ -565,7 +586,7 @@ namespace NzbDrone.Core.Indexers.Definitions if (_settings.UseFilenameForSingleEpisodes) { - var files = torrent.Files; + var files = torrent.Files.ToList(); if (files.Count > 1) { @@ -603,11 +624,13 @@ namespace NzbDrone.Core.Indexers.Definitions } } + var useYearInTitle = year is > 0 && torrent.Files.Any(f => f.FileName.Contains(year.Value.ToString())); + foreach (var title in synonyms) { var releaseTitle = groupName is "Movie" or "Live Action Movie" ? $"{releaseGroup}{title} {year} {infoString}" : - $"{releaseGroup}{title} {releaseInfo} {infoString}"; + $"{releaseGroup}{title}{(useYearInTitle ? $" {year}" : string.Empty)} {releaseInfo} {infoString}"; var guid = new Uri(details + "?nh=" + HashUtil.CalculateMd5(title)); @@ -644,16 +667,16 @@ namespace NzbDrone.Core.Indexers.Definitions private static int? ParseSeasonFromTitles(IReadOnlyCollection titles) { - var advancedSeasonRegex = new Regex(@"(\d+)(st|nd|rd|th) Season", RegexOptions.Compiled | RegexOptions.IgnoreCase); + var advancedSeasonRegex = new Regex(@"\b(?:(?\d+)(?:st|nd|rd|th) Season|Season (?\d+))\b", RegexOptions.Compiled | RegexOptions.IgnoreCase); var seasonCharactersRegex = new Regex(@"(I{2,})$", RegexOptions.Compiled); - var seasonNumberRegex = new Regex(@"\b(?:S)?([2-9])$", RegexOptions.Compiled); + var seasonNumberRegex = new Regex(@"\b(?[2-9])$", RegexOptions.Compiled); foreach (var title in titles) { var advancedSeasonRegexMatch = advancedSeasonRegex.Match(title); if (advancedSeasonRegexMatch.Success) { - return ParseUtil.CoerceInt(advancedSeasonRegexMatch.Groups[1].Value); + return ParseUtil.CoerceInt(advancedSeasonRegexMatch.Groups["season"].Value); } var seasonCharactersRegexMatch = seasonCharactersRegex.Match(title); @@ -665,7 +688,7 @@ namespace NzbDrone.Core.Indexers.Definitions var seasonNumberRegexMatch = seasonNumberRegex.Match(title); if (seasonNumberRegexMatch.Success) { - return ParseUtil.CoerceInt(seasonNumberRegexMatch.Groups[1].Value); + return ParseUtil.CoerceInt(seasonNumberRegexMatch.Groups["season"].Value); } } @@ -751,7 +774,9 @@ namespace NzbDrone.Core.Indexers.Definitions public int Matches { get; set; } [JsonPropertyName("Groups")] - public AnimeBytesGroup[] Groups { get; set; } + public IReadOnlyCollection Groups { get; set; } + + public string Error { get; set; } } public class AnimeBytesGroup @@ -779,16 +804,16 @@ namespace NzbDrone.Core.Indexers.Definitions public string Image { get; set; } [JsonPropertyName("SynonymnsV2")] - public Dictionary Synonymns { get; set; } + public IReadOnlyDictionary Synonymns { get; set; } [JsonPropertyName("Description")] public string Description { get; set; } [JsonPropertyName("Tags")] - public List Tags { get; set; } + public IReadOnlyCollection Tags { get; set; } [JsonPropertyName("Torrents")] - public List Torrents { get; set; } + public IReadOnlyCollection Torrents { get; set; } } public class AnimeBytesTorrent @@ -827,7 +852,7 @@ namespace NzbDrone.Core.Indexers.Definitions public int FileCount { get; set; } [JsonPropertyName("FileList")] - public List Files { get; set; } + public IReadOnlyCollection Files { get; set; } [JsonPropertyName("UploadTime")] public string UploadTime { get; set; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/AnimeTorrents.cs b/src/NzbDrone.Core/Indexers/Definitions/AnimeTorrents.cs index 7ba07ab80..14b64979b 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/AnimeTorrents.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/AnimeTorrents.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; -using System.Threading.Tasks; using AngleSharp.Html.Parser; using NLog; using NzbDrone.Common.Extensions; @@ -44,46 +43,19 @@ namespace NzbDrone.Core.Indexers.Definitions return new AnimeTorrentsParser(Settings, Capabilities.Categories); } - protected override async Task DoLogin() - { - UpdateCookies(null, null); - - var loginUrl = Settings.BaseUrl + "login.php"; - - var loginPage = await ExecuteAuth(new HttpRequest(loginUrl)); - - var requestBuilder = new HttpRequestBuilder(loginUrl) - { - LogResponseContent = true, - AllowAutoRedirect = true - }; - - var authLoginRequest = requestBuilder - .Post() - .SetCookies(loginPage.GetCookies()) - .AddFormParameter("username", Settings.Username) - .AddFormParameter("password", Settings.Password) - .AddFormParameter("form", "login") - .AddFormParameter("rememberme[]", "1") - .SetHeader("Content-Type", "application/x-www-form-urlencoded") - .SetHeader("Referer", loginUrl) - .Build(); - - var response = await ExecuteAuth(authLoginRequest); - - if (response.Content == null || !response.Content.Contains("logout.php")) - { - throw new IndexerAuthException("AnimeTorrents authentication failed"); - } - - UpdateCookies(response.GetCookies(), DateTime.Now.AddDays(30)); - - _logger.Debug("AnimeTorrents authentication succeeded"); - } - protected override bool CheckIfLoginNeeded(HttpResponse httpResponse) { - return httpResponse.Content.Contains("Access Denied!") || httpResponse.Content.Contains("login.php"); + if (httpResponse.Content.Contains("Access Denied!") || httpResponse.Content.Contains("login.php")) + { + throw new IndexerAuthException("AnimeTorrents authentication with cookies failed."); + } + + return false; + } + + protected override IDictionary GetCookies() + { + return CookieUtil.CookieHeaderToDictionary(Settings.Cookie); } private IndexerCapabilities SetCapabilities() @@ -119,6 +91,7 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(17, NewznabStandardCategory.BooksComics, "Doujinshi"); caps.Categories.AddCategoryMapping(18, NewznabStandardCategory.BooksComics, "Doujinshi 18+"); caps.Categories.AddCategoryMapping(19, NewznabStandardCategory.Audio, "OST"); + caps.Categories.AddCategoryMapping(20, NewznabStandardCategory.AudioAudiobook, "Audiobooks"); return caps; } @@ -291,7 +264,7 @@ namespace NzbDrone.Core.Indexers.Definitions var qTitleLink = row.QuerySelector("td:nth-of-type(2) a:nth-of-type(1)"); var title = qTitleLink?.TextContent.Trim(); - // If we search an get no results, we still get a table just with no info. + // If we search and get no results, we still get a table just with no info. if (title.IsNullOrWhiteSpace()) { break; @@ -306,6 +279,8 @@ namespace NzbDrone.Core.Indexers.Definitions var connections = row.QuerySelector("td:nth-of-type(8)").TextContent.Trim().Split('/', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); var seeders = ParseUtil.CoerceInt(connections[0]); + var leechers = ParseUtil.CoerceInt(connections[1]); + var grabs = ParseUtil.CoerceInt(connections[2]); var categoryLink = row.QuerySelector("td:nth-of-type(1) a")?.GetAttribute("href") ?? string.Empty; var categoryId = ParseUtil.GetArgumentFromQueryString(categoryLink, "cat"); @@ -327,17 +302,17 @@ namespace NzbDrone.Core.Indexers.Definitions PublishDate = publishedDate, Size = ParseUtil.GetBytes(row.QuerySelector("td:nth-of-type(6)").TextContent.Trim()), Seeders = seeders, - Peers = ParseUtil.CoerceInt(connections[1]) + seeders, - Grabs = ParseUtil.CoerceInt(connections[2]), + Peers = leechers + seeders, + Grabs = grabs, DownloadVolumeFactor = downloadVolumeFactor, UploadVolumeFactor = 1, Genres = row.QuerySelectorAll("td:nth-of-type(2) a.tortags").Select(t => t.TextContent.Trim()).ToList() }; - var uLFactorImg = row.QuerySelector("img[alt*=\"x Multiplier Torrent\"]"); - if (uLFactorImg != null) + var uploadFactor = row.QuerySelector("img[alt*=\"x Multiplier Torrent\"]")?.GetAttribute("alt"); + if (uploadFactor != null) { - release.UploadVolumeFactor = ParseUtil.CoerceDouble(uLFactorImg.GetAttribute("alt").Split('x')[0]); + release.UploadVolumeFactor = ParseUtil.CoerceDouble(uploadFactor.Split('x')[0]); } releaseInfos.Add(release); @@ -349,7 +324,7 @@ namespace NzbDrone.Core.Indexers.Definitions public Action, DateTime?> CookiesUpdater { get; set; } } - public class AnimeTorrentsSettings : UserPassTorrentBaseSettings + public class AnimeTorrentsSettings : CookieTorrentBaseSettings { public AnimeTorrentsSettings() { @@ -360,7 +335,7 @@ namespace NzbDrone.Core.Indexers.Definitions [FieldDefinition(4, Label = "Freeleech Only", Type = FieldType.Checkbox, HelpText = "Show freeleech torrents only")] public bool FreeleechOnly { get; set; } - [FieldDefinition(5, Label = "Downloadable Only", Type = FieldType.Checkbox, HelpText = "Search downloadable torrents only (enable this only if your account class is Newbie)")] + [FieldDefinition(5, Label = "Downloadable Only", Type = FieldType.Checkbox, HelpText = "Search downloadable torrents only (enable this only if your account class is Newbie)", Advanced = true)] public bool DownloadableOnly { get; set; } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/AroLol.cs b/src/NzbDrone.Core/Indexers/Definitions/AroLol.cs deleted file mode 100644 index a3d68a63f..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/AroLol.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Collections.Generic; -using AngleSharp.Html.Parser; -using NLog; -using NzbDrone.Common.Http; -using NzbDrone.Core.Annotations; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Indexers.Definitions.Gazelle; -using NzbDrone.Core.Indexers.Exceptions; -using NzbDrone.Core.Messaging.Events; - -namespace NzbDrone.Core.Indexers.Definitions; - -[Obsolete("Site has shutdown")] -public class AroLol : GazelleBase -{ - public override string Name => "aro.lol"; - public override string[] IndexerUrls => new[] { "https://aro.lol/" }; - public override string Description => "aro.lol is a SERBIAN/ENGLISH Private Torrent Tracker for ANIME"; - public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - - public AroLol(IIndexerHttpClient httpClient, - IEventAggregator eventAggregator, - IIndexerStatusService indexerStatusService, - IConfigService configService, - Logger logger) - : base(httpClient, eventAggregator, indexerStatusService, configService, logger) - { - } - - protected override HttpRequestBuilder AuthLoginRequestBuilder() - { - return base.AuthLoginRequestBuilder() - .AddFormParameter("twofa", Settings.TwoFactorAuthCode?.Trim() ?? ""); - } - - protected override bool CheckForLoginError(HttpResponse response) - { - if (response.Content.Contains("loginform")) - { - var parser = new HtmlParser(); - using var dom = parser.ParseDocument(response.Content); - var errorMessage = dom.QuerySelector("#loginform > .warning")?.TextContent.Trim(); - - throw new IndexerAuthException(errorMessage ?? "Unknown error message, please report."); - } - - return true; - } - - protected override IndexerCapabilities SetCapabilities() - { - var caps = new IndexerCapabilities - { - TvSearchParams = new List - { - TvSearchParam.Q - }, - MovieSearchParams = new List - { - MovieSearchParam.Q - }, - BookSearchParams = new List - { - BookSearchParam.Q - } - }; - - caps.Categories.AddCategoryMapping("1", NewznabStandardCategory.Movies, "Movies"); - caps.Categories.AddCategoryMapping("2", NewznabStandardCategory.TVAnime, "Anime"); - caps.Categories.AddCategoryMapping("3", NewznabStandardCategory.Books, "Manga"); - caps.Categories.AddCategoryMapping("4", NewznabStandardCategory.Console, "Games"); - caps.Categories.AddCategoryMapping("5", NewznabStandardCategory.Other, "Other"); - - return caps; - } -} - -public class AroLolSettings : GazelleSettings -{ - [FieldDefinition(4, Label = "2FA code", Type = FieldType.Textbox, HelpText = "Only fill in the 2FA code box if you have enabled 2FA on the aro.lol Web Site. Otherwise just leave it empty.")] - public string TwoFactorAuthCode { get; set; } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazParserBase.cs b/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazParserBase.cs index dd68952a5..56cc7a3fe 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazParserBase.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazParserBase.cs @@ -33,6 +33,12 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz throw new RequestLimitReachedException(indexerResponse, "API Request Limit Reached"); } + if (indexerResponse.HttpResponse.StatusCode == HttpStatusCode.Unauthorized) + { + STJson.TryDeserialize(indexerResponse.HttpResponse.Content, out var errorResponse); + throw new IndexerAuthException(errorResponse?.Message ?? "Unauthorized request to indexer"); + } + if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK) { throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request"); diff --git a/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazSettings.cs index b2740c64a..d2dbc52d1 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazSettings.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazSettings.cs @@ -27,16 +27,16 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz public string Token { get; set; } - [FieldDefinition(2, Label = "Username", HelpText = "Site Username", Privacy = PrivacyLevel.UserName)] + [FieldDefinition(2, Label = "Username", HelpText = "IndexerAvistazSettingsUsernameHelpText", HelpTextWarning = "IndexerAvistazSettingsUsernameHelpTextWarning", Privacy = PrivacyLevel.UserName)] public string Username { get; set; } - [FieldDefinition(3, Label = "Password", HelpText = "Site Password", Privacy = PrivacyLevel.Password, Type = FieldType.Password)] + [FieldDefinition(3, Label = "Password", HelpText = "IndexerAvistazSettingsPasswordHelpText", Privacy = PrivacyLevel.Password, Type = FieldType.Password)] public string Password { get; set; } - [FieldDefinition(4, Label = "PID", HelpText = "PID from My Account or My Profile page", Privacy = PrivacyLevel.Password, Type = FieldType.Password)] + [FieldDefinition(4, Label = "PID", HelpText = "IndexerAvistazSettingsPidHelpText", Privacy = PrivacyLevel.Password, Type = FieldType.Password)] public string Pid { get; set; } - [FieldDefinition(5, Label = "Freeleech Only", Type = FieldType.Checkbox, HelpText = "Search freeleech only")] + [FieldDefinition(5, Label = "IndexerSettingsFreeleechOnly", Type = FieldType.Checkbox, HelpText = "IndexerAvistazSettingsFreeleechOnlyHelpText")] public bool FreeleechOnly { get; set; } public override NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Indexers/Definitions/BakaBT.cs b/src/NzbDrone.Core/Indexers/Definitions/BakaBT.cs index ecfe03140..e80e35743 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/BakaBT.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/BakaBT.cs @@ -46,7 +46,7 @@ namespace NzbDrone.Core.Indexers.Definitions return new BakaBTParser(Settings, Capabilities.Categories); } - public override async Task Download(Uri link) + public override async Task Download(Uri link) { var request = new HttpRequestBuilder(link.ToString()) .SetCookies(GetCookies() ?? new Dictionary()) diff --git a/src/NzbDrone.Core/Indexers/Definitions/BeyondHD.cs b/src/NzbDrone.Core/Indexers/Definitions/BeyondHD.cs index cb7d2cf5f..3fc4a3328 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/BeyondHD.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/BeyondHD.cs @@ -45,7 +45,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IParseIndexerResponse GetParser() { - return new BeyondHDParser(Capabilities.Categories); + return new BeyondHDParser(Settings, Capabilities.Categories); } protected override IList CleanupReleases(IEnumerable releases, SearchCriteriaBase searchCriteria) @@ -55,7 +55,7 @@ namespace NzbDrone.Core.Indexers.Definitions return FilterReleasesByQuery(cleanReleases, searchCriteria).ToList(); } - private IndexerCapabilities SetCapabilities() + private static IndexerCapabilities SetCapabilities() { var caps = new IndexerCapabilities { @@ -69,7 +69,8 @@ namespace NzbDrone.Core.Indexers.Definitions }, Flags = new List { - IndexerFlag.Internal + IndexerFlag.Internal, + IndexerFlag.Exclusive, } }; @@ -91,7 +92,7 @@ namespace NzbDrone.Core.Indexers.Definitions _capabilities = capabilities; } - private IEnumerable GetPagedRequests(SearchCriteriaBase searchCriteria, string term, string imdbId = null, int tmdbId = 0) + private IEnumerable GetPagedRequests(SearchCriteriaBase searchCriteria, string searchTerm, string imdbId = null, int tmdbId = 0) { var body = new Dictionary { @@ -128,9 +129,9 @@ namespace NzbDrone.Core.Indexers.Definitions body.Add("tmdb_id", tmdbId); } - if (term.IsNotNullOrWhiteSpace()) + if (searchTerm.IsNotNullOrWhiteSpace()) { - body.Add("search", term); + body.Add("search", searchTerm.Trim()); } var cats = _capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories); @@ -198,7 +199,16 @@ namespace NzbDrone.Core.Indexers.Definitions { var pageableRequests = new IndexerPageableRequestChain(); - pageableRequests.Add(GetPagedRequests(searchCriteria, searchCriteria.SanitizedTvSearchString, searchCriteria.FullImdbId)); + var searchTerm = searchCriteria.SanitizedTvSearchString; + + if (searchCriteria.Season is > 0 && + searchCriteria.Episode.IsNotNullOrWhiteSpace() && + DateTime.TryParseExact($"{searchCriteria.Season} {searchCriteria.Episode}", "yyyy MM/dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var showDate)) + { + searchTerm = $"{searchCriteria.SanitizedSearchTerm} {showDate.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)}"; + } + + pageableRequests.Add(GetPagedRequests(searchCriteria, searchTerm, searchCriteria.FullImdbId)); return pageableRequests; } @@ -227,10 +237,12 @@ namespace NzbDrone.Core.Indexers.Definitions public class BeyondHDParser : IParseIndexerResponse { + private readonly BeyondHDSettings _settings; private readonly IndexerCapabilitiesCategories _categories; - public BeyondHDParser(IndexerCapabilitiesCategories categories) + public BeyondHDParser(BeyondHDSettings settings, IndexerCapabilitiesCategories categories) { + _settings = settings; _categories = categories; } @@ -264,20 +276,15 @@ namespace NzbDrone.Core.Indexers.Definitions foreach (var row in jsonResponse.Results) { + // Skip invalid results when freeleech or limited filtering is set + if ((_settings.FreeleechOnly && !row.Freeleech) || (_settings.LimitedOnly && !row.Limited)) + { + continue; + } + var details = row.InfoUrl; var link = row.DownloadLink; - // BHD can return crazy values for tmdb - var tmdbId = row.TmdbId.IsNullOrWhiteSpace() ? 0 : ParseUtil.TryCoerceInt(row.TmdbId.Split("/")[1], out var tmdbResult) ? tmdbResult : 0; - var imdbId = ParseUtil.GetImdbId(row.ImdbId).GetValueOrDefault(); - - var flags = new HashSet(); - - if (row.Internal) - { - flags.Add(IndexerFlag.Internal); - } - var release = new TorrentInfo { Title = row.Name, @@ -287,19 +294,27 @@ namespace NzbDrone.Core.Indexers.Definitions Guid = details, Categories = _categories.MapTrackerCatDescToNewznab(row.Category), PublishDate = DateTime.Parse(row.CreatedAt, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal), - IndexerFlags = flags, + IndexerFlags = GetIndexerFlags(row), Size = row.Size, Grabs = row.Grabs, Seeders = row.Seeders, - ImdbId = imdbId, - TmdbId = tmdbId, + ImdbId = ParseUtil.GetImdbId(row.ImdbId).GetValueOrDefault(), Peers = row.Leechers + row.Seeders, DownloadVolumeFactor = row.Freeleech || row.Limited ? 0 : row.Promo75 ? 0.25 : row.Promo50 ? 0.5 : row.Promo25 ? 0.75 : 1, UploadVolumeFactor = 1, MinimumRatio = 1, MinimumSeedTime = 172800, // 120 hours + Languages = row.Audios?.Split(",", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + Subs = row.Subtitles?.Split(",", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), }; + // BHD can return crazy values for tmdb + if (row.TmdbId.IsNotNullOrWhiteSpace()) + { + var tmdbId = row.TmdbId.Split("/").ElementAtOrDefault(1); + release.TmdbId = tmdbId != null && ParseUtil.TryCoerceInt(tmdbId, out var tmdbResult) ? tmdbResult : 0; + } + releaseInfos.Add(release); } @@ -309,6 +324,23 @@ namespace NzbDrone.Core.Indexers.Definitions .ToArray(); } + private static HashSet GetIndexerFlags(BeyondHDTorrent item) + { + var flags = new HashSet(); + + if (item.Internal) + { + flags.Add(IndexerFlag.Internal); + } + + if (item.Exclusive) + { + flags.Add(IndexerFlag.Exclusive); + } + + return flags; + } + public Action, DateTime?> CookiesUpdater { get; set; } } @@ -316,8 +348,8 @@ namespace NzbDrone.Core.Indexers.Definitions { public BeyondHDSettingsValidator() { - RuleFor(c => c.ApiKey).NotEmpty(); - RuleFor(c => c.RssKey).NotEmpty(); + RuleFor(c => c.ApiKey).NotEmpty().Length(32); + RuleFor(c => c.RssKey).NotEmpty().Length(32); } } @@ -440,9 +472,13 @@ namespace NzbDrone.Core.Indexers.Definitions [JsonPropertyName("times_completed")] public int Grabs { get; set; } + public int Seeders { get; set; } public int Leechers { get; set; } + public string Audios { get; set; } + public string Subtitles { get; set; } + [JsonPropertyName("created_at")] public string CreatedAt { get; set; } @@ -468,6 +504,8 @@ namespace NzbDrone.Core.Indexers.Definitions public bool Limited { get; set; } + public bool Exclusive { get; set; } + public bool Internal { get; set; } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetParser.cs b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetParser.cs index 394fed9ad..60c2c0a58 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetParser.cs @@ -88,7 +88,7 @@ namespace NzbDrone.Core.Indexers.BroadcastheNet Guid = $"BTN-{torrent.TorrentID}", InfoUrl = $"{protocol}//broadcasthe.net/torrents.php?id={torrent.GroupID}&torrentid={torrent.TorrentID}", DownloadUrl = RegexProtocol.Replace(torrent.DownloadURL, protocol), - Title = CleanReleaseName(torrent.ReleaseName), + Title = GetTitle(torrent), Categories = _categories.MapTrackerCatToNewznab(torrent.Resolution), InfoHash = torrent.InfoHash, Size = torrent.Size, @@ -136,9 +136,17 @@ namespace NzbDrone.Core.Indexers.BroadcastheNet return releaseInfos; } - private string CleanReleaseName(string releaseName) + private static string GetTitle(BroadcastheNetTorrent torrent) { - return releaseName.Replace("\\", ""); + var releaseName = torrent.ReleaseName.Replace("\\", ""); + + if (torrent.Container.ToUpperInvariant() is "M2TS" or "ISO") + { + releaseName = Regex.Replace(releaseName, @"\b(H\.?265)\b", "HEVC", RegexOptions.Compiled); + releaseName = Regex.Replace(releaseName, @"\b(H\.?264)\b", "AVC", RegexOptions.Compiled); + } + + return releaseName; } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetRequestGenerator.cs index 74d7d7251..9b6d93499 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/BroadcastheNet/BroadcastheNetRequestGenerator.cs @@ -74,7 +74,7 @@ namespace NzbDrone.Core.Indexers.BroadcastheNet else if (DateTime.TryParseExact($"{searchCriteria.Season} {searchCriteria.Episode}", "yyyy MM/dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var showDate)) { // Daily Episode - parameters.Name = showDate.ToString("yyyy.MM.dd"); + parameters.Name = showDate.ToString("yyyy.MM.dd", CultureInfo.InvariantCulture); parameters.Category = "Episode"; pageableRequests.Add(GetPagedRequests(parameters, btnResults, btnOffset)); } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs index 45553786f..80c70c068 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs @@ -4,10 +4,8 @@ using System.Linq; using System.Threading.Tasks; using FluentValidation.Results; using NLog; -using NzbDrone.Common; using NzbDrone.Common.Cache; using NzbDrone.Common.Http; -using NzbDrone.Common.Serializer; using NzbDrone.Core.Configuration; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerVersions; @@ -24,7 +22,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann private readonly ICached _generatorCache; public override string Name => "Cardigann"; - public override string[] IndexerUrls => new string[] { "" }; + public override string[] IndexerUrls => new[] { "" }; public override string Description => ""; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; @@ -49,8 +47,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann public override IIndexerRequestGenerator GetRequestGenerator() { - var cacheKey = $"{Settings.DefinitionFile}.{HashUtil.ComputeSha256Hash(Settings.ToJson())}"; - var generator = _generatorCache.Get(cacheKey, () => + var generator = _generatorCache.Get(Settings.DefinitionFile, () => new CardigannRequestGenerator(_configService, _definitionService.GetCachedDefinition(Settings.DefinitionFile), _logger, @@ -61,10 +58,11 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann Settings = Settings }); - generator = (CardigannRequestGenerator)SetCookieFunctions(generator); - + generator.Definition = Definition; generator.Settings = Settings; + generator = (CardigannRequestGenerator)SetCookieFunctions(generator); + _generatorCache.ClearExpired(); return generator; @@ -86,7 +84,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann if (_definitionService.GetCachedDefinition(Settings.DefinitionFile).Search?.Rows?.Filters?.Any(x => x.Name == "andmatch") ?? false) { - cleanReleases = FilterReleasesByQuery(releases, searchCriteria).ToList(); + cleanReleases = FilterReleasesByQuery(cleanReleases, searchCriteria).ToList(); } return cleanReleases; diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannBase.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannBase.cs index 166275514..9e2c6e06b 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannBase.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannBase.cs @@ -8,6 +8,7 @@ using System.Text; using System.Text.RegularExpressions; using AngleSharp.Dom; using Microsoft.AspNetCore.WebUtilities; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; using NzbDrone.Common.Extensions; @@ -139,20 +140,13 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann { var selectorSelector = ApplyGoTemplateText(selector.Selector, variables); - if (dom.Matches(selectorSelector)) - { - selection = dom; - } - else - { - selection = QuerySelector(dom, selectorSelector); - } + selection = dom.Matches(selectorSelector) ? dom : QuerySelector(dom, selectorSelector); if (selection == null) { if (required) { - throw new Exception(string.Format("Selector \"{0}\" didn't match {1}", selectorSelector, dom.ToHtmlPretty())); + throw new Exception($"Selector \"{selectorSelector}\" didn't match {dom.ToHtmlPretty()}"); } return null; @@ -195,7 +189,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann { if (required) { - throw new Exception(string.Format("Attribute \"{0}\" is not set for element {1}", selector.Attribute, selection.ToHtmlPretty())); + throw new Exception($"Attribute \"{selector.Attribute}\" is not set for element {selection.ToHtmlPretty()}"); } return null; @@ -221,19 +215,19 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann if (selector.Selector != null) { var selectorSelector = ApplyGoTemplateText(selector.Selector.TrimStart('.'), variables); - selectorSelector = JsonParseFieldSelector(parentObj, selectorSelector); + var fieldSelector = JsonParseFieldSelector(parentObj, selectorSelector); JToken selection = null; - if (selectorSelector != null) + if (fieldSelector != null) { - selection = parentObj.SelectToken(selectorSelector); + selection = parentObj.SelectToken(fieldSelector); } if (selection == null) { if (required) { - throw new Exception(string.Format("Selector \"{0}\" didn't match {1}", selectorSelector, parentObj.ToString())); + throw new Exception($"Selector \"{selectorSelector}\" didn't match {parentObj.ToString(Formatting.None)}"); } return null; @@ -241,7 +235,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann if (selection.Type is JTokenType.Array) { - // turn this json array into a comma delimited string + // turn this json array into a comma-delimited string var valueArray = selection.Value(); value = string.Join(",", valueArray); } @@ -266,7 +260,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann { if (required) { - throw new Exception($"None of the case selectors \"{string.Join(",", selector.Case)}\" matched {parentObj}"); + throw new Exception($"None of the case selectors \"{string.Join(",", selector.Case)}\" matched {parentObj.ToString(Formatting.None)}"); } return null; @@ -337,9 +331,12 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann variables[name] = selected.Key; break; case "info": - variables[name] = value; - break; + case "info_cookie": + case "info_flaresolverr": + case "info_useragent": + case "info_category_8000": case "cardigannCaptcha": + // no-op break; default: throw new NotSupportedException($"Type {setting.Type} is not supported."); diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannParser.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannParser.cs index 8edc7d6de..4b96e25ed 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannParser.cs @@ -39,22 +39,22 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann var indexerLogging = _configService.LogIndexerResponse; - if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK) + if (indexerResponse.HttpResponse.HasHttpRedirect && indexerResponse.HttpResponse.RedirectUrl.IsNotNullOrWhiteSpace()) { - if (indexerResponse.HttpResponse.HasHttpRedirect) + _logger.Warn("Redirected to {0} from indexer request", indexerResponse.HttpResponse.RedirectUrl); + + if (indexerResponse.HttpResponse.RedirectUrl.ContainsIgnoreCase("/login.php")) { - _logger.Warn("Redirected to {0} from indexer request", indexerResponse.HttpResponse.RedirectUrl); - - if (indexerResponse.HttpResponse.RedirectUrl.ContainsIgnoreCase("/login.php")) - { - // Remove cookie cache - CookiesUpdater(null, null); - throw new IndexerException(indexerResponse, "We are being redirected to the login page. Most likely your session expired or was killed. Recheck your cookie or credentials and try testing the indexer."); - } - - throw new IndexerException(indexerResponse, $"Redirected to {indexerResponse.HttpResponse.RedirectUrl} from indexer request"); + // Remove cookie cache + CookiesUpdater(null, null); + throw new IndexerException(indexerResponse, "We are being redirected to the login page. Most likely your session expired or was killed. Recheck your cookie or credentials and try testing the indexer."); } + throw new IndexerException(indexerResponse, $"Redirected to {indexerResponse.HttpResponse.RedirectUrl} from indexer request"); + } + + if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK) + { throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request"); } @@ -105,7 +105,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann } catch (Exception ex) { - _logger.Trace(ex, "Failed to parse JSON rows count."); + _logger.Debug(ex, "Failed to parse JSON rows count."); } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequestGenerator.cs index 72cccca19..11c8060ab 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequestGenerator.cs @@ -212,7 +212,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann } } - var loginUrl = ResolvePath(login.Path).ToString(); + var loginUrl = ResolvePath(ApplyGoTemplateText(login.Path, variables)).ToString(); CookiesUpdater(null, null); @@ -253,7 +253,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann } else if (login.Method == "form") { - var loginUrl = ResolvePath(login.Path).ToString(); + var loginUrl = ResolvePath(ApplyGoTemplateText(login.Path, variables)).ToString(); var queryCollection = new NameValueCollection(); var pairs = new Dictionary(); @@ -332,37 +332,47 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann } // selector inputs - if (login.Selectorinputs != null) + if (login.Selectorinputs != null && login.Selectorinputs.Any()) { - foreach (var selectorinput in login.Selectorinputs) + foreach (var selectorInput in login.Selectorinputs) { - string value = null; try { - value = HandleSelector(selectorinput.Value, landingResultDocument.FirstElementChild); - pairs[selectorinput.Key] = value; + var value = HandleSelector(selectorInput.Value, landingResultDocument.FirstElementChild, required: !selectorInput.Value.Optional); + + if (selectorInput.Value.Optional && value == null) + { + continue; + } + + pairs[selectorInput.Key] = value; } catch (Exception ex) { - throw new CardigannException(string.Format("Error while parsing selector input={0}, selector={1}, value={2}: {3}", selectorinput.Key, selectorinput.Value.Selector, value, ex.Message)); + throw new CardigannException($"Error while parsing selector input={selectorInput.Key}, selector={selectorInput.Value.Selector}: {ex.Message}", ex); } } } // getselector inputs - if (login.Getselectorinputs != null) + if (login.Getselectorinputs != null && login.Getselectorinputs.Any()) { - foreach (var selectorinput in login.Getselectorinputs) + foreach (var selectorInput in login.Getselectorinputs) { - string value = null; try { - value = HandleSelector(selectorinput.Value, landingResultDocument.FirstElementChild); - queryCollection[selectorinput.Key] = value; + var value = HandleSelector(selectorInput.Value, landingResultDocument.FirstElementChild, required: !selectorInput.Value.Optional); + + if (selectorInput.Value.Optional && value == null) + { + continue; + } + + queryCollection[selectorInput.Key] = value; } catch (Exception ex) { - throw new CardigannException(string.Format("Error while parsing get selector input={0}, selector={1}, value={2}: {3}", selectorinput.Key, selectorinput.Value.Selector, value, ex.Message)); + throw new CardigannException($"Error while parsing get selector input={selectorInput.Key}, selector={selectorInput.Value.Selector}: {ex.Message}", ex); } } } @@ -524,7 +534,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann } } - var loginUrl = ResolvePath(login.Path + "?" + queryCollection.GetQueryString()).ToString(); + var loginUrl = ResolvePath(ApplyGoTemplateText(login.Path, variables) + "?" + queryCollection.GetQueryString()).ToString(); CookiesUpdater(null, null); @@ -553,7 +563,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann else if (login.Method == "oneurl") { var oneUrl = ApplyGoTemplateText(login.Inputs["oneurl"]); - var loginUrl = ResolvePath(login.Path + oneUrl).ToString(); + var loginUrl = ResolvePath(ApplyGoTemplateText(login.Path, variables) + oneUrl).ToString(); CookiesUpdater(null, null); @@ -629,7 +639,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann var variables = GetBaseTemplateVariables(); var headers = ParseCustomHeaders(_definition.Login?.Headers ?? _definition.Search?.Headers, variables); - var loginUrl = ResolvePath(login.Path); + var loginUrl = ResolvePath(ApplyGoTemplateText(login.Path, variables)); var requestBuilder = new HttpRequestBuilder(loginUrl.AbsoluteUri) { @@ -690,7 +700,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann var captchaElement = landingResultDocument.QuerySelector(captcha.Selector); if (captchaElement != null) { - var loginUrl = ResolvePath(login.Path); + var loginUrl = ResolvePath(ApplyGoTemplateText(login.Path, variables)); var captchaUrl = ResolvePath(captchaElement.GetAttribute("src"), loginUrl); var request = new HttpRequestBuilder(captchaUrl.ToString()) @@ -1178,14 +1188,14 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann if (method == HttpMethod.Get && searchUrls.Contains(searchUrl)) { - _logger.Trace("Skip duplicated request {0}", searchUrl); + _logger.Trace("Skip duplicated request for {0}: {1}", Definition.Name, searchUrl); continue; } searchUrls.Add(searchUrl); - _logger.Debug($"Adding request: {searchUrl}"); + _logger.Debug("Adding request for {0}: {1}", Definition.Name, searchUrl); var requestBuilder = new HttpRequestBuilder(searchUrl) { diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannSettings.cs index 0e6912ad2..51d352649 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannSettings.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannSettings.cs @@ -1,11 +1,23 @@ using System.Collections.Generic; +using FluentValidation; using NzbDrone.Core.Annotations; using NzbDrone.Core.Indexers.Settings; +using NzbDrone.Core.Validation; namespace NzbDrone.Core.Indexers.Definitions.Cardigann { + public class CardigannSettingsValidator : NoAuthSettingsValidator + { + public CardigannSettingsValidator() + { + RuleFor(c => c.DefinitionFile).NotEmpty(); + } + } + public class CardigannSettings : NoAuthTorrentBaseSettings { + private static readonly CardigannSettingsValidator Validator = new (); + public CardigannSettings() { ExtraFieldData = new Dictionary(); @@ -15,5 +27,10 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann public string DefinitionFile { get; set; } public Dictionary ExtraFieldData { get; set; } + + public override NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileList.cs b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileList.cs index c2494aa15..ca34098a8 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileList.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileList.cs @@ -103,6 +103,8 @@ public class FileList : TorrentIndexerBase caps.Categories.AddCategoryMapping(25, NewznabStandardCategory.Movies3D, "Filme 3D"); caps.Categories.AddCategoryMapping(26, NewznabStandardCategory.MoviesBluRay, "Filme 4K Blu-Ray"); caps.Categories.AddCategoryMapping(27, NewznabStandardCategory.TVUHD, "Seriale 4K"); + caps.Categories.AddCategoryMapping(28, NewznabStandardCategory.MoviesForeign, "RO Dubbed"); + caps.Categories.AddCategoryMapping(28, NewznabStandardCategory.TVForeign, "RO Dubbed"); return caps; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListParser.cs b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListParser.cs index 7b2e8cf32..6d91d930d 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListParser.cs @@ -61,7 +61,7 @@ public class FileListParser : IParseIndexerResponse var imdbId = 0; if (row.ImdbId is { Length: > 2 }) { - imdbId = int.Parse(row.ImdbId.Substring(2)); + int.TryParse(row.ImdbId.TrimStart('t'), out imdbId); } var downloadVolumeFactor = row.FreeLeech ? 0 : 1; @@ -101,7 +101,7 @@ public class FileListParser : IParseIndexerResponse var url = new HttpUri(_settings.BaseUrl) .CombinePath("/download.php") .AddQueryParam("id", torrentId.ToString()) - .AddQueryParam("passkey", _settings.Passkey); + .AddQueryParam("passkey", _settings.Passkey.Trim()); return url.FullUri; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListRequestGenerator.cs index 08937d679..ccbe2c356 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListRequestGenerator.cs @@ -151,7 +151,7 @@ public class FileListRequestGenerator : IIndexerRequestGenerator if (searchCriteria.Categories != null && searchCriteria.Categories.Any()) { - parameters.Set("category", string.Join(",", Capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories))); + parameters.Set("category", string.Join(",", Capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories).Distinct().ToList())); } if (Settings.FreeleechOnly) diff --git a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleApi.cs b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleApi.cs index 0703a00d2..89df33663 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleApi.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleApi.cs @@ -95,3 +95,8 @@ public class GazelleIndexResponse public string Authkey { get; set; } public string Passkey { get; set; } } + +public class GazelleErrorResponse +{ + public string Error { get; init; } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleBase.cs b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleBase.cs index 711b2e152..584efd1e5 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleBase.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleBase.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; using NLog; +using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Messaging.Events; @@ -82,28 +83,30 @@ public abstract class GazelleBase : TorrentIndexerBase protected virtual bool CheckForLoginError(HttpResponse response) => true; - public override async Task Download(Uri link) + public override async Task Download(Uri link) { - var response = await base.Download(link); + var downloadResponse = await base.Download(link); - if (response.Length >= 1 - && response[0] != 'd' // simple test for torrent vs HTML content + var fileData = downloadResponse.Data; + + if (Settings.UseFreeleechToken == (int)GazelleFreeleechTokenAction.Preferred + && fileData.Length >= 1 + && fileData[0] != 'd' // simple test for torrent vs HTML content && link.Query.Contains("usetoken=1")) { - var html = Encoding.GetString(response); + var html = Encoding.GetString(fileData); + if (html.Contains("You do not have any freeleech tokens left.") || html.Contains("You do not have enough freeleech tokens") || html.Contains("This torrent is too large.") || html.Contains("You cannot use tokens here")) { - // download again without usetoken=1 - var requestLinkNew = link.ToString().Replace("&usetoken=1", ""); - - response = await base.Download(new Uri(requestLinkNew)); + // Try to download again without usetoken + downloadResponse = await base.Download(link.RemoveQueryParam("usetoken")); } } - return response; + return downloadResponse; } protected override IDictionary GetCookies() diff --git a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleParser.cs b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleParser.cs index f24954d18..1682dea35 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleParser.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; +using NzbDrone.Common.Serializer; using NzbDrone.Core.Indexers.Exceptions; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; @@ -32,7 +33,9 @@ public class GazelleParser : IParseIndexerResponse // Remove cookie cache CookiesUpdater(null, null); - throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request"); + STJson.TryDeserialize(indexerResponse.Content, out var errorResponse); + + throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request: {errorResponse?.Error ?? "Check the logs for more information."}"); } if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value)) @@ -59,8 +62,10 @@ public class GazelleParser : IParseIndexerResponse { foreach (var torrent in result.Torrents) { + var isFreeLeech = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsPersonalFreeLeech; + // skip releases that cannot be used with freeleech tokens when the option is enabled - if (Settings.UseFreeleechToken && !torrent.CanUseToken) + if (Settings.UseFreeleechToken == (int)GazelleFreeleechTokenAction.Required && !torrent.CanUseToken && !isFreeLeech) { continue; } @@ -79,7 +84,7 @@ public class GazelleParser : IParseIndexerResponse { Guid = infoUrl, InfoUrl = infoUrl, - DownloadUrl = GetDownloadUrl(id, !torrent.IsFreeLeech && !torrent.IsNeutralLeech && !torrent.IsPersonalFreeLeech), + DownloadUrl = GetDownloadUrl(id, torrent.CanUseToken && !isFreeLeech), Title = WebUtility.HtmlDecode(title), Container = torrent.Encoding, Files = torrent.FileCount, @@ -91,7 +96,7 @@ public class GazelleParser : IParseIndexerResponse PublishDate = torrent.Time.ToUniversalTime(), Scene = torrent.Scene, PosterUrl = posterUrl, - DownloadVolumeFactor = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsPersonalFreeLeech ? 0 : 1, + DownloadVolumeFactor = isFreeLeech ? 0 : 1, UploadVolumeFactor = torrent.IsNeutralLeech ? 0 : 1 }; @@ -110,8 +115,10 @@ public class GazelleParser : IParseIndexerResponse } else { + var isFreeLeech = result.IsFreeLeech || result.IsNeutralLeech || result.IsPersonalFreeLeech; + // skip releases that cannot be used with freeleech tokens when the option is enabled - if (Settings.UseFreeleechToken && !result.CanUseToken) + if (Settings.UseFreeleechToken == (int)GazelleFreeleechTokenAction.Required && !result.CanUseToken && !isFreeLeech) { continue; } @@ -124,7 +131,7 @@ public class GazelleParser : IParseIndexerResponse { Guid = infoUrl, InfoUrl = infoUrl, - DownloadUrl = GetDownloadUrl(id, !result.IsFreeLeech && !result.IsNeutralLeech && !result.IsPersonalFreeLeech), + DownloadUrl = GetDownloadUrl(id, result.CanUseToken && !isFreeLeech), Title = groupName, Size = long.Parse(result.Size), Seeders = int.Parse(result.Seeders), @@ -133,7 +140,7 @@ public class GazelleParser : IParseIndexerResponse Grabs = result.Snatches, PublishDate = long.TryParse(result.GroupTime, out var num) ? DateTimeOffset.FromUnixTimeSeconds(num).UtcDateTime : DateTimeUtil.FromFuzzyTime((string)result.GroupTime), PosterUrl = posterUrl, - DownloadVolumeFactor = result.IsFreeLeech || result.IsNeutralLeech || result.IsPersonalFreeLeech ? 0 : 1, + DownloadVolumeFactor = isFreeLeech ? 0 : 1, UploadVolumeFactor = result.IsNeutralLeech ? 0 : 1 }; @@ -165,7 +172,7 @@ public class GazelleParser : IParseIndexerResponse .AddQueryParam("action", "download") .AddQueryParam("id", torrentId); - if (Settings.UseFreeleechToken) + if (Settings.UseFreeleechToken is (int)GazelleFreeleechTokenAction.Preferred or (int)GazelleFreeleechTokenAction.Required && canUseToken) { url = url.AddQueryParam("usetoken", "1"); } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleSettings.cs index 64a394a77..628436549 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleSettings.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleSettings.cs @@ -16,11 +16,23 @@ public class GazelleSettings : UserPassTorrentBaseSettings public string AuthKey { get; set; } public string PassKey { get; set; } - [FieldDefinition(5, Type = FieldType.Checkbox, Label = "Use Freeleech Token", HelpText = "Use freeleech tokens when available")] - public bool UseFreeleechToken { get; set; } + [FieldDefinition(5, Type = FieldType.Select, Label = "Use Freeleech Tokens", SelectOptions = typeof(GazelleFreeleechTokenAction), HelpText = "When to use freeleech tokens")] + public int UseFreeleechToken { get; set; } public override NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); } } + +public enum GazelleFreeleechTokenAction +{ + [FieldOption(Label = "Never", Hint = "Do not use tokens")] + Never = 0, + + [FieldOption(Label = "Preferred", Hint = "Use token if possible")] + Preferred = 1, + + [FieldOption(Label = "Required", Hint = "Abort download if unable to use token")] + Required = 2, +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/GazelleGames.cs b/src/NzbDrone.Core/Indexers/Definitions/GazelleGames.cs index e3f9513ae..55d52bace 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/GazelleGames.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/GazelleGames.cs @@ -4,6 +4,7 @@ using System.Collections.ObjectModel; using System.Linq; using System.Net; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; using FluentValidation; using FluentValidation.Results; @@ -368,6 +369,8 @@ namespace NzbDrone.Core.Indexers.Definitions private readonly GazelleGamesSettings _settings; private readonly IndexerCapabilitiesCategories _categories; + private static Regex YearRegex => new (@"\b(?:19|20|21)\d{2}\b", RegexOptions.Compiled); + public GazelleGamesParser(GazelleGamesSettings settings, IndexerCapabilitiesCategories categories) { _settings = settings; @@ -469,14 +472,14 @@ namespace NzbDrone.Core.Indexers.Definitions { var title = WebUtility.HtmlDecode(torrent.ReleaseTitle); - if (group.Year is > 0 && !title.Contains(group.Year.ToString())) + if (group.Year is > 0 && title.IsNotNullOrWhiteSpace() && !YearRegex.Match(title).Success) { title += $" ({group.Year})"; } if (torrent.RemasterTitle.IsNotNullOrWhiteSpace()) { - title += $" [{$"{torrent.RemasterTitle} {torrent.RemasterYear}".Trim()}]"; + title += $" [{$"{WebUtility.HtmlDecode(torrent.RemasterTitle)} {torrent.RemasterYear}".Trim()}]"; } var flags = new List diff --git a/src/NzbDrone.Core/Indexers/Definitions/GreatPosterWall.cs b/src/NzbDrone.Core/Indexers/Definitions/GreatPosterWall.cs index 765de7aa7..29e01057f 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/GreatPosterWall.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/GreatPosterWall.cs @@ -7,6 +7,7 @@ using Newtonsoft.Json; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; +using NzbDrone.Common.Serializer; using NzbDrone.Core.Annotations; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers.Definitions.Gazelle; @@ -148,7 +149,9 @@ public class GreatPosterWallParser : GazelleParser throw new IndexerException(indexerResponse, $"Redirected to {indexerResponse.HttpResponse.RedirectUrl} from indexer request"); } - throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request"); + STJson.TryDeserialize(indexerResponse.Content, out var errorResponse); + + throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request: {errorResponse?.Error ?? "Check the logs for more information."}"); } if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value)) @@ -168,8 +171,10 @@ public class GreatPosterWallParser : GazelleParser { foreach (var torrent in result.Torrents) { + var isFreeLeech = torrent.IsFreeleech || torrent.IsNeutralLeech || torrent.IsPersonalFreeleech; + // skip releases that cannot be used with freeleech tokens when the option is enabled - if (_settings.UseFreeleechToken && !torrent.CanUseToken) + if (_settings.UseFreeleechToken == (int)GazelleFreeleechTokenAction.Required && !torrent.CanUseToken && !isFreeLeech) { continue; } @@ -181,7 +186,7 @@ public class GreatPosterWallParser : GazelleParser { Guid = infoUrl, InfoUrl = infoUrl, - DownloadUrl = GetDownloadUrl(torrent.TorrentId, !torrent.IsFreeleech && !torrent.IsNeutralLeech && !torrent.IsPersonalFreeleech), + DownloadUrl = GetDownloadUrl(torrent.TorrentId, torrent.CanUseToken && !isFreeLeech), Title = WebUtility.HtmlDecode(torrent.FileName).Trim(), PosterUrl = GetPosterUrl(result.Cover), PublishDate = new DateTimeOffset(time, TimeSpan.FromHours(8)).UtcDateTime, // Time is Chinese Time, add 8 hours difference from UTC @@ -192,7 +197,7 @@ public class GreatPosterWallParser : GazelleParser Grabs = torrent.Snatches, Files = torrent.FileCount, Scene = torrent.Scene, - DownloadVolumeFactor = torrent.IsFreeleech || torrent.IsNeutralLeech || torrent.IsPersonalFreeleech ? 0 : 1, + DownloadVolumeFactor = isFreeLeech ? 0 : 1, UploadVolumeFactor = torrent.IsNeutralLeech ? 0 : 1, MinimumRatio = 1, MinimumSeedTime = 172800 // 48 hours @@ -240,7 +245,7 @@ public class GreatPosterWallParser : GazelleParser .AddQueryParam("action", "download") .AddQueryParam("id", torrentId); - if (_settings.UseFreeleechToken && canUseToken) + if (_settings.UseFreeleechToken is (int)GazelleFreeleechTokenAction.Preferred or (int)GazelleFreeleechTokenAction.Required && canUseToken) { url = url.AddQueryParam("usetoken", "1"); } diff --git a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBits.cs b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBits.cs index 84ea1be25..b4d924511 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBits.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBits.cs @@ -32,7 +32,7 @@ namespace NzbDrone.Core.Indexers.Definitions.HDBits return new HDBitsParser(Settings, Capabilities.Categories); } - private IndexerCapabilities SetCapabilities() + private static IndexerCapabilities SetCapabilities() { var caps = new IndexerCapabilities { @@ -43,6 +43,11 @@ namespace NzbDrone.Core.Indexers.Definitions.HDBits MovieSearchParams = new List { MovieSearchParam.Q, MovieSearchParam.ImdbId + }, + Flags = new List + { + IndexerFlag.Internal, + IndexerFlag.Exclusive, } }; diff --git a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsApi.cs b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsApi.cs index c66c6213e..5f8b947ae 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsApi.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsApi.cs @@ -85,6 +85,9 @@ namespace NzbDrone.Core.Indexers.Definitions.HDBits [JsonProperty(PropertyName = "type_origin")] public int TypeOrigin { get; set; } + [JsonProperty(PropertyName = "type_exclusive")] + public int TypeExclusive { get; set; } + [JsonProperty(PropertyName = "imdb")] public ImdbInfo ImdbInfo { get; set; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsInfo.cs b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsInfo.cs deleted file mode 100644 index 412295abf..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsInfo.cs +++ /dev/null @@ -1,9 +0,0 @@ -using NzbDrone.Core.Parser.Model; - -namespace NzbDrone.Core.Indexers.Definitions.HDBits -{ - public class HDBitsInfo : TorrentInfo - { - public bool? Internal { get; set; } - } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsParser.cs b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsParser.cs index b157aaf9b..8b0b55319 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsParser.cs @@ -62,16 +62,8 @@ namespace NzbDrone.Core.Indexers.Definitions.HDBits } var id = result.Id; - var internalRelease = result.TypeOrigin == 1; - var flags = new HashSet(); - - if (internalRelease) - { - flags.Add(IndexerFlag.Internal); - } - - releaseInfos.Add(new HDBitsInfo + releaseInfos.Add(new TorrentInfo { Guid = $"HDBits-{id}", Title = GetTitle(result), @@ -85,28 +77,43 @@ namespace NzbDrone.Core.Indexers.Definitions.HDBits Files = (int)result.NumFiles, Peers = result.Leechers + result.Seeders, PublishDate = result.Added.ToUniversalTime(), - Internal = internalRelease, Year = result.ImdbInfo?.Year ?? 0, ImdbId = result.ImdbInfo?.Id ?? 0, TvdbId = result.TvdbInfo?.Id ?? 0, DownloadVolumeFactor = GetDownloadVolumeFactor(result), UploadVolumeFactor = GetUploadVolumeFactor(result), - IndexerFlags = flags + IndexerFlags = GetIndexerFlags(result) }); } return releaseInfos.ToArray(); } - public Action, DateTime?> CookiesUpdater { get; set; } - private string GetTitle(TorrentQueryResponse item) { - return _settings.UseFilenames && item.FileName.IsNotNullOrWhiteSpace() + // Use release name for XXX content and full discs + return item.TypeCategory != 7 && item.TypeMedium != 1 && _settings.UseFilenames && item.FileName.IsNotNullOrWhiteSpace() ? item.FileName.Replace(".torrent", "", StringComparison.InvariantCultureIgnoreCase) : item.Name; } + private static HashSet GetIndexerFlags(TorrentQueryResponse item) + { + var flags = new HashSet(); + + if (item.TypeOrigin == 1) + { + flags.Add(IndexerFlag.Internal); + } + + if (item.TypeExclusive == 1) + { + flags.Add(IndexerFlag.Exclusive); + } + + return flags; + } + private double GetDownloadVolumeFactor(TorrentQueryResponse item) { if (item.FreeLeech == "yes") @@ -153,5 +160,7 @@ namespace NzbDrone.Core.Indexers.Definitions.HDBits return url.FullUri; } + + public Action, DateTime?> CookiesUpdater { get; set; } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsRequestGenerator.cs index c94595221..87e24b1c2 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsRequestGenerator.cs @@ -66,7 +66,7 @@ namespace NzbDrone.Core.Indexers.Definitions.HDBits if (DateTime.TryParseExact($"{searchCriteria.Season} {searchCriteria.Episode}", "yyyy MM/dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var showDate)) { - query.Search = showDate.ToString("yyyy-MM-dd"); + query.Search = showDate.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture); } else { diff --git a/src/NzbDrone.Core/Indexers/Definitions/HDSpace.cs b/src/NzbDrone.Core/Indexers/Definitions/HDSpace.cs index 486f69448..8b2933799 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/HDSpace.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/HDSpace.cs @@ -20,6 +20,7 @@ using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers.Definitions { + [Obsolete("Moved to YML for Cardigann")] public class HDSpace : TorrentIndexerBase { public override string Name => "HD-Space"; diff --git a/src/NzbDrone.Core/Indexers/Definitions/HDTorrents.cs b/src/NzbDrone.Core/Indexers/Definitions/HDTorrents.cs index a16db1721..47b01f8cd 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/HDTorrents.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/HDTorrents.cs @@ -338,8 +338,7 @@ namespace NzbDrone.Core.Indexers.Definitions dlVolumeFactor = 0.25; } - var imdbLink = row.QuerySelector("a[href*=\"www.imdb.com/title/\"]")?.GetAttribute("href"); - var imdb = !string.IsNullOrWhiteSpace(imdbLink) ? ParseUtil.GetImdbId(imdbLink) : null; + var imdbId = ParseUtil.GetImdbId(row.QuerySelector("a[href*=\"www.imdb.com/title/\"]")?.GetAttribute("href")?.TrimEnd('/')?.Split('/')?.LastOrDefault()); var flags = new HashSet(); @@ -358,7 +357,7 @@ namespace NzbDrone.Core.Indexers.Definitions PosterUrl = poster, PublishDate = publishDate, Categories = _categories.MapTrackerCatToNewznab(cat), - ImdbId = imdb ?? 0, + ImdbId = imdbId ?? 0, Size = size, Grabs = grabs, Seeders = seeders, diff --git a/src/NzbDrone.Core/Indexers/Definitions/Headphones/Headphones.cs b/src/NzbDrone.Core/Indexers/Definitions/Headphones/Headphones.cs index c2e66ae08..f8028c0b8 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Headphones/Headphones.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Headphones/Headphones.cs @@ -50,28 +50,33 @@ namespace NzbDrone.Core.Indexers.Headphones } } - public override async Task Download(Uri link) + public override async Task Download(Uri link) { var requestBuilder = new HttpRequestBuilder(link.AbsoluteUri); - var downloadBytes = Array.Empty(); - var request = requestBuilder.Build(); request.Credentials = new BasicNetworkCredential(Settings.Username, Settings.Password); + byte[] downloadBytes; + long elapsedTime; + try { var response = await _httpClient.ExecuteProxiedAsync(request, Definition); downloadBytes = response.ResponseData; + elapsedTime = response.ElapsedTime; } catch (Exception) { _indexerStatusService.RecordFailure(Definition.Id); _logger.Error("Download failed"); + throw; } - return downloadBytes; + ValidateDownloadData(downloadBytes); + + return new IndexerDownloadResponse(downloadBytes, elapsedTime); } private IndexerCapabilities SetCapabilities() diff --git a/src/NzbDrone.Core/Indexers/Definitions/Headphones/HeadphonesRssParser.cs b/src/NzbDrone.Core/Indexers/Definitions/Headphones/HeadphonesRssParser.cs index 8e0292f3c..94903adb8 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Headphones/HeadphonesRssParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Headphones/HeadphonesRssParser.cs @@ -70,16 +70,17 @@ namespace NzbDrone.Core.Indexers.Headphones protected override bool PostProcess(IndexerResponse indexerResponse, List items, List releases) { var enclosureTypes = items.SelectMany(GetEnclosures).Select(v => v.Type).Distinct().ToArray(); + if (enclosureTypes.Any() && enclosureTypes.Intersect(PreferredEnclosureMimeTypes).Empty()) { if (enclosureTypes.Intersect(TorrentEnclosureMimeTypes).Any()) { - _logger.Warn("Feed does not contain {0}, found {1}, did you intend to add a Torznab indexer?", NzbEnclosureMimeType, enclosureTypes[0]); - } - else - { - _logger.Warn("Feed does not contain {0}, found {1}.", NzbEnclosureMimeType, enclosureTypes[0]); + _logger.Warn("{0} does not contain {1}, found {2}, did you intend to add a Torznab indexer?", indexerResponse.Request.Url, NzbEnclosureMimeType, enclosureTypes[0]); + + return false; } + + _logger.Warn("{0} does not contain {1}, found {2}.", indexerResponse.Request.Url, NzbEnclosureMimeType, enclosureTypes[0]); } return true; diff --git a/src/NzbDrone.Core/Indexers/Definitions/IPTorrents.cs b/src/NzbDrone.Core/Indexers/Definitions/IPTorrents.cs index 305335761..d77c26935 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/IPTorrents.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/IPTorrents.cs @@ -189,7 +189,7 @@ namespace NzbDrone.Core.Indexers.Definitions var qc = new NameValueCollection(); - foreach (var cat in Capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories)) + foreach (var cat in Capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories).Distinct()) { qc.Add(cat, string.Empty); } @@ -203,10 +203,13 @@ namespace NzbDrone.Core.Indexers.Definitions { // ipt uses sphinx, which supports boolean operators and grouping qc.Add("q", "+(" + imdbId + ")"); + + // search in description + qc.Add("qf", "all"); } - // changed from else if to if to support searching imdbid + season/episode in the same query - if (!string.IsNullOrWhiteSpace(term)) + // changed from "else if" to "if" to support searching imdbid + season/episode in the same query + if (term.IsNotNullOrWhiteSpace()) { // similar to above qc.Add("q", "+(" + term + ")"); @@ -400,10 +403,13 @@ namespace NzbDrone.Core.Indexers.Definitions private static string CleanTitle(string title) { - // drop invalid chars that seems to have cropped up in some titles. #6582 + // Drop invalid chars that seems to have cropped up in some titles. #6582 title = Regex.Replace(title, @"[\u0000-\u0008\u000A-\u001F\u0100-\uFFFF]", string.Empty, RegexOptions.Compiled); title = Regex.Replace(title, @"[\(\[\{]REQ(UEST(ED)?)?[\)\]\}]", string.Empty, RegexOptions.Compiled | RegexOptions.IgnoreCase); + // Drop languages between brackets conflicting with anime release group parsing + title = Regex.Replace(title, @"^\[[a-z0-9 ._-]+\][-._ ](?.*-[a-z0-9]+)$", "${title}", RegexOptions.Compiled | RegexOptions.IgnoreCase); + return title.Trim(' ', '-', ':'); } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/ImmortalSeed.cs b/src/NzbDrone.Core/Indexers/Definitions/ImmortalSeed.cs index d904c4d05..26f5ada43 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/ImmortalSeed.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/ImmortalSeed.cs @@ -80,7 +80,7 @@ namespace NzbDrone.Core.Indexers.Definitions protected override bool CheckIfLoginNeeded(HttpResponse httpResponse) { - return httpResponse.Content.Contains("You do not have permission to access this page."); + return !httpResponse.Content.Contains("logout.php"); } private IndexerCapabilities SetCapabilities() diff --git a/src/NzbDrone.Core/Indexers/Definitions/Knaben.cs b/src/NzbDrone.Core/Indexers/Definitions/Knaben.cs new file mode 100644 index 000000000..286ef1a1a --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/Knaben.cs @@ -0,0 +1,349 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text.Json.Serialization; +using System.Text.RegularExpressions; +using NLog; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Http; +using NzbDrone.Common.Serializer; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Indexers.Exceptions; +using NzbDrone.Core.Indexers.Settings; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.Parser.Model; +using static Newtonsoft.Json.Formatting; + +namespace NzbDrone.Core.Indexers.Definitions +{ + public class Knaben : TorrentIndexerBase<NoAuthTorrentBaseSettings> + { + public override string Name => "Knaben"; + public override string[] IndexerUrls => new[] { "https://knaben.org/" }; + public override string[] LegacyUrls => new[] { "https://knaben.eu/" }; + public override string Description => "Knaben is a Public torrent meta-search engine"; + public override IndexerPrivacy Privacy => IndexerPrivacy.Public; + public override IndexerCapabilities Capabilities => SetCapabilities(); + + public Knaben(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + : base(httpClient, eventAggregator, indexerStatusService, configService, logger) + { + } + + public override IIndexerRequestGenerator GetRequestGenerator() + { + return new KnabenRequestGenerator(Capabilities); + } + + public override IParseIndexerResponse GetParser() + { + return new KnabenParser(Capabilities.Categories); + } + + private static IndexerCapabilities SetCapabilities() + { + var caps = new IndexerCapabilities + { + TvSearchParams = new List<TvSearchParam> + { + TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep + }, + MovieSearchParams = new List<MovieSearchParam> + { + MovieSearchParam.Q + }, + MusicSearchParams = new List<MusicSearchParam> + { + MusicSearchParam.Q + }, + BookSearchParams = new List<BookSearchParam> + { + BookSearchParam.Q + } + }; + + caps.Categories.AddCategoryMapping(1000000, NewznabStandardCategory.Audio, "Audio"); + caps.Categories.AddCategoryMapping(1001000, NewznabStandardCategory.AudioMP3, "MP3"); + caps.Categories.AddCategoryMapping(1002000, NewznabStandardCategory.AudioLossless, "Lossless"); + caps.Categories.AddCategoryMapping(1003000, NewznabStandardCategory.AudioAudiobook, "Audiobook"); + caps.Categories.AddCategoryMapping(1004000, NewznabStandardCategory.AudioVideo, "Audio Video"); + caps.Categories.AddCategoryMapping(1005000, NewznabStandardCategory.AudioOther, "Radio"); + caps.Categories.AddCategoryMapping(1006000, NewznabStandardCategory.AudioOther, "Audio Other"); + caps.Categories.AddCategoryMapping(2000000, NewznabStandardCategory.TV, "TV"); + caps.Categories.AddCategoryMapping(2001000, NewznabStandardCategory.TVHD, "TV HD"); + caps.Categories.AddCategoryMapping(2002000, NewznabStandardCategory.TVSD, "TV SD"); + caps.Categories.AddCategoryMapping(2003000, NewznabStandardCategory.TVUHD, "TV UHD"); + caps.Categories.AddCategoryMapping(2004000, NewznabStandardCategory.TVDocumentary, "Documentary"); + caps.Categories.AddCategoryMapping(2005000, NewznabStandardCategory.TVForeign, "TV Foreign"); + caps.Categories.AddCategoryMapping(2006000, NewznabStandardCategory.TVSport, "Sport"); + caps.Categories.AddCategoryMapping(2007000, NewznabStandardCategory.TVOther, "Cartoon"); + caps.Categories.AddCategoryMapping(2008000, NewznabStandardCategory.TVOther, "TV Other"); + caps.Categories.AddCategoryMapping(3000000, NewznabStandardCategory.Movies, "Movies"); + caps.Categories.AddCategoryMapping(3001000, NewznabStandardCategory.MoviesHD, "Movies HD"); + caps.Categories.AddCategoryMapping(3002000, NewznabStandardCategory.MoviesSD, "Movies SD"); + caps.Categories.AddCategoryMapping(3003000, NewznabStandardCategory.MoviesUHD, "Movies UHD"); + caps.Categories.AddCategoryMapping(3004000, NewznabStandardCategory.MoviesDVD, "Movies DVD"); + caps.Categories.AddCategoryMapping(3005000, NewznabStandardCategory.MoviesForeign, "Movies Foreign"); + caps.Categories.AddCategoryMapping(3006000, NewznabStandardCategory.MoviesForeign, "Movies Bollywood"); + caps.Categories.AddCategoryMapping(3007000, NewznabStandardCategory.Movies3D, "Movies 3D"); + caps.Categories.AddCategoryMapping(3008000, NewznabStandardCategory.MoviesOther, "Movies Other"); + caps.Categories.AddCategoryMapping(4000000, NewznabStandardCategory.PC, "PC"); + caps.Categories.AddCategoryMapping(4001000, NewznabStandardCategory.PCGames, "Games"); + caps.Categories.AddCategoryMapping(4002000, NewznabStandardCategory.PC0day, "Software"); + caps.Categories.AddCategoryMapping(4003000, NewznabStandardCategory.PCMac, "Mac"); + caps.Categories.AddCategoryMapping(4004000, NewznabStandardCategory.PCISO, "Unix"); + caps.Categories.AddCategoryMapping(5000000, NewznabStandardCategory.XXX, "XXX"); + caps.Categories.AddCategoryMapping(5001000, NewznabStandardCategory.XXXx264, "XXX Video"); + caps.Categories.AddCategoryMapping(5002000, NewznabStandardCategory.XXXImageSet, "XXX ImageSet"); + caps.Categories.AddCategoryMapping(5003000, NewznabStandardCategory.XXXOther, "XXX Games"); + caps.Categories.AddCategoryMapping(5004000, NewznabStandardCategory.XXXOther, "XXX Hentai"); + caps.Categories.AddCategoryMapping(5005000, NewznabStandardCategory.XXXOther, "XXX Other"); + caps.Categories.AddCategoryMapping(6000000, NewznabStandardCategory.TVAnime, "Anime"); + caps.Categories.AddCategoryMapping(6001000, NewznabStandardCategory.TVAnime, "Anime Subbed"); + caps.Categories.AddCategoryMapping(6002000, NewznabStandardCategory.TVAnime, "Anime Dubbed"); + caps.Categories.AddCategoryMapping(6003000, NewznabStandardCategory.TVAnime, "Anime Dual audio"); + caps.Categories.AddCategoryMapping(6004000, NewznabStandardCategory.TVAnime, "Anime Raw"); + caps.Categories.AddCategoryMapping(6005000, NewznabStandardCategory.AudioVideo, "Music Video"); + caps.Categories.AddCategoryMapping(6006000, NewznabStandardCategory.BooksOther, "Literature"); + caps.Categories.AddCategoryMapping(6007000, NewznabStandardCategory.AudioOther, "Music"); + caps.Categories.AddCategoryMapping(6008000, NewznabStandardCategory.TVAnime, "Anime non-english translated"); + caps.Categories.AddCategoryMapping(7000000, NewznabStandardCategory.Console, "Console"); + caps.Categories.AddCategoryMapping(7001000, NewznabStandardCategory.ConsolePS4, "PS4"); + caps.Categories.AddCategoryMapping(7002000, NewznabStandardCategory.ConsolePS3, "PS3"); + caps.Categories.AddCategoryMapping(7003000, NewznabStandardCategory.ConsolePS3, "PS2"); + caps.Categories.AddCategoryMapping(7004000, NewznabStandardCategory.ConsolePS3, "PS1"); + caps.Categories.AddCategoryMapping(7005000, NewznabStandardCategory.ConsolePSVita, "PS Vita"); + caps.Categories.AddCategoryMapping(7006000, NewznabStandardCategory.ConsolePSP, "PSP"); + caps.Categories.AddCategoryMapping(7007000, NewznabStandardCategory.ConsoleXBox360, "Xbox 360"); + caps.Categories.AddCategoryMapping(7008000, NewznabStandardCategory.ConsoleXBox, "Xbox"); + caps.Categories.AddCategoryMapping(7009000, NewznabStandardCategory.ConsoleNDS, "Switch"); + caps.Categories.AddCategoryMapping(7010000, NewznabStandardCategory.ConsoleNDS, "NDS"); + caps.Categories.AddCategoryMapping(7011000, NewznabStandardCategory.ConsoleWii, "Wii"); + caps.Categories.AddCategoryMapping(7012000, NewznabStandardCategory.ConsoleWiiU, "WiiU"); + caps.Categories.AddCategoryMapping(7013000, NewznabStandardCategory.Console3DS, "3DS"); + caps.Categories.AddCategoryMapping(7014000, NewznabStandardCategory.ConsoleWii, "GameCube"); + caps.Categories.AddCategoryMapping(7015000, NewznabStandardCategory.ConsoleOther, "Other"); + caps.Categories.AddCategoryMapping(8000000, NewznabStandardCategory.PCMobileOther, "Mobile"); + caps.Categories.AddCategoryMapping(8001000, NewznabStandardCategory.PCMobileAndroid, "Android"); + caps.Categories.AddCategoryMapping(8002000, NewznabStandardCategory.PCMobileiOS, "IOS"); + caps.Categories.AddCategoryMapping(8003000, NewznabStandardCategory.PCMobileOther, "PC Other"); + caps.Categories.AddCategoryMapping(9000000, NewznabStandardCategory.Books, "Books"); + caps.Categories.AddCategoryMapping(9001000, NewznabStandardCategory.BooksEBook, "EBooks"); + caps.Categories.AddCategoryMapping(9002000, NewznabStandardCategory.BooksComics, "Comics"); + caps.Categories.AddCategoryMapping(9003000, NewznabStandardCategory.BooksMags, "Magazines"); + caps.Categories.AddCategoryMapping(9004000, NewznabStandardCategory.BooksTechnical, "Technical"); + caps.Categories.AddCategoryMapping(9005000, NewznabStandardCategory.BooksOther, "Books Other"); + caps.Categories.AddCategoryMapping(10000000, NewznabStandardCategory.Other, "Other"); + caps.Categories.AddCategoryMapping(10001000, NewznabStandardCategory.OtherMisc, "Other Misc"); + + return caps; + } + } + + public class KnabenRequestGenerator : IIndexerRequestGenerator + { + private const string ApiSearchEndpoint = "https://api.knaben.org/v1"; + + private readonly IndexerCapabilities _capabilities; + + public KnabenRequestGenerator(IndexerCapabilities capabilities) + { + _capabilities = capabilities; + } + + public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(CreateRequest(searchCriteria, searchCriteria.SanitizedSearchTerm)); + + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(CreateRequest(searchCriteria, searchCriteria.SanitizedSearchTerm)); + + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(CreateRequest(searchCriteria, searchCriteria.SanitizedTvSearchString)); + + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(CreateRequest(searchCriteria, searchCriteria.SanitizedSearchTerm)); + + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(CreateRequest(searchCriteria, searchCriteria.SanitizedSearchTerm)); + + return pageableRequests; + } + + private IEnumerable<IndexerRequest> CreateRequest(SearchCriteriaBase searchCriteria, string searchTerm) + { + var body = new Dictionary<string, object> + { + { "order_by", "date" }, + { "order_direction", "desc" }, + { "from", 0 }, + { "size", 100 }, + { "hide_unsafe", true } + }; + + var searchQuery = searchTerm.Trim(); + + if (searchQuery.IsNotNullOrWhiteSpace()) + { + body.Add("search_type", "100%"); + body.Add("search_field", "title"); + body.Add("query", searchQuery); + } + + var categories = _capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories); + + if (categories is { Count: > 0 }) + { + body.Add("categories", categories.Select(int.Parse).Distinct().ToArray()); + } + + var request = new HttpRequest(ApiSearchEndpoint, HttpAccept.Json) + { + Headers = + { + ContentType = "application/json" + }, + Method = HttpMethod.Post + }; + request.SetContent(body.ToJson()); + request.ContentSummary = body.ToJson(None); + + yield return new IndexerRequest(request); + } + + public Func<IDictionary<string, string>> GetCookies { get; set; } + public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; } + } + + public class KnabenParser : IParseIndexerResponse + { + private static readonly Regex DateTimezoneRegex = new (@"[+-]\d{2}:\d{2}$", RegexOptions.Compiled); + + private readonly IndexerCapabilitiesCategories _categories; + + public KnabenParser(IndexerCapabilitiesCategories categories) + { + _categories = categories; + } + + public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse) + { + var indexerHttpResponse = indexerResponse.HttpResponse; + + if (indexerHttpResponse.StatusCode != HttpStatusCode.OK) + { + throw new IndexerException(indexerResponse, $"Unexpected response status {indexerHttpResponse.StatusCode} code from indexer request"); + } + + if (!indexerHttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value)) + { + throw new IndexerException(indexerResponse, $"Unexpected response header {indexerHttpResponse.Headers.ContentType} from indexer request, expected {HttpAccept.Json.Value}"); + } + + var releaseInfos = new List<ReleaseInfo>(); + + var jsonResponse = STJson.Deserialize<KnabenResponse>(indexerResponse.Content); + + if (jsonResponse?.Hits == null) + { + return releaseInfos; + } + + var rows = jsonResponse.Hits.Where(r => r.Seeders > 0).ToList(); + + foreach (var row in rows) + { + // Not all entries have the TZ in the "date" field + var publishDate = row.Date.IsNotNullOrWhiteSpace() && !DateTimezoneRegex.IsMatch(row.Date) ? $"{row.Date}+01:00" : row.Date; + + var releaseInfo = new TorrentInfo + { + Guid = row.InfoUrl, + Title = row.Title, + InfoUrl = row.InfoUrl, + DownloadUrl = row.DownloadUrl.IsNotNullOrWhiteSpace() ? row.DownloadUrl : null, + MagnetUrl = row.MagnetUrl.IsNotNullOrWhiteSpace() ? row.MagnetUrl : null, + Categories = row.CategoryIds.SelectMany(cat => _categories.MapTrackerCatToNewznab(cat.ToString())).Distinct().ToList(), + InfoHash = row.InfoHash, + Size = row.Size, + Seeders = row.Seeders, + Peers = row.Leechers + row.Seeders, + PublishDate = DateTime.Parse(publishDate, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal), + DownloadVolumeFactor = 0, + UploadVolumeFactor = 1 + }; + + releaseInfos.Add(releaseInfo); + } + + // order by date + return releaseInfos; + } + + public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; } + } + + internal sealed class KnabenResponse + { + public IReadOnlyCollection<KnabenRelease> Hits { get; init; } = Array.Empty<KnabenRelease>(); + } + + internal sealed class KnabenRelease + { + public string Title { get; init; } + + [JsonPropertyName("categoryId")] + public IReadOnlyCollection<int> CategoryIds { get; init; } = Array.Empty<int>(); + + [JsonPropertyName("hash")] + public string InfoHash { get; init; } + + [JsonPropertyName("details")] + public string InfoUrl { get; init; } + + [JsonPropertyName("link")] + public string DownloadUrl { get; init; } + + public string MagnetUrl { get; init; } + + [JsonPropertyName("bytes")] + public long Size { get; init; } + + public int Seeders { get; init; } + + [JsonPropertyName("peers")] + public int Leechers { get; init; } + + public string Date { get; init; } + } +} diff --git a/src/NzbDrone.Core/Indexers/Definitions/MTeamTp.cs b/src/NzbDrone.Core/Indexers/Definitions/MTeamTp.cs index 2be146ddc..c8f08a87d 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/MTeamTp.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/MTeamTp.cs @@ -19,7 +19,6 @@ using NzbDrone.Core.Indexers.Exceptions; using NzbDrone.Core.Indexers.Settings; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Messaging.Events; -using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Validation; @@ -51,15 +50,15 @@ public class MTeamTp : TorrentIndexerBase<MTeamTpSettings> public override IIndexerRequestGenerator GetRequestGenerator() { - return new MTeamTpRequestGenerator(Settings, Capabilities); + return new MTeamTpRequestGenerator(Settings, Capabilities, BuildApiUrl(Settings)); } public override IParseIndexerResponse GetParser() { - return new MTeamTpParser(Settings, Capabilities.Categories); + return new MTeamTpParser(Settings, Capabilities.Categories, BuildApiUrl(Settings)); } - public override async Task<byte[]> Download(Uri link) + public override async Task<IndexerDownloadResponse> Download(Uri link) { var request = new HttpRequestBuilder(link.ToString()) .SetHeader("x-api-key", Settings.ApiKey) @@ -168,19 +167,26 @@ public class MTeamTp : TorrentIndexerBase<MTeamTpSettings> return caps; } + + private static string BuildApiUrl(IIndexerSettings settings) + { + return $"https://api.{settings.BaseUrl.AsSpan(settings.BaseUrl.IndexOf('.') + 1)}"; + } } public class MTeamTpRequestGenerator : IIndexerRequestGenerator { private readonly MTeamTpSettings _settings; private readonly IndexerCapabilities _capabilities; + private readonly string _apiUrl; private readonly int[] _trackerAdultCategories = { 410, 429, 424, 430, 426, 437, 431, 432, 436, 425, 433, 411, 412, 413, 440 }; - public MTeamTpRequestGenerator(MTeamTpSettings settings, IndexerCapabilities capabilities) + public MTeamTpRequestGenerator(MTeamTpSettings settings, IndexerCapabilities capabilities, string apiUrl) { _settings = settings; _capabilities = capabilities; + _apiUrl = apiUrl; } public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) @@ -253,7 +259,7 @@ public class MTeamTpRequestGenerator : IIndexerRequestGenerator private IndexerRequest BuildSearchRequest(MTeamTpRequestType requestType, IEnumerable<int> categoryMapping, string searchTerm, string imdbId) { - var request = new HttpRequestBuilder(_settings.BaseUrl) + var request = new HttpRequestBuilder(_apiUrl) .Resource("/api/torrent/search") .SetHeader("x-api-key", _settings.ApiKey) .Accept(HttpAccept.Json) @@ -299,11 +305,13 @@ public class MTeamTpParser : IParseIndexerResponse { private readonly MTeamTpSettings _settings; private readonly IndexerCapabilitiesCategories _categories; + private readonly string _apiUrl; - public MTeamTpParser(MTeamTpSettings settings, IndexerCapabilitiesCategories categories) + public MTeamTpParser(MTeamTpSettings settings, IndexerCapabilitiesCategories categories, string apiUrl) { _settings = settings; _categories = categories; + _apiUrl = apiUrl; } public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse) @@ -329,6 +337,13 @@ public class MTeamTpParser : IParseIndexerResponse if (jsonResponse?.Data?.Torrents == null) { + if (jsonResponse != null && + jsonResponse.Message.IsNotNullOrWhiteSpace() && + jsonResponse.Message.ToUpperInvariant() != "SUCCESS") + { + throw new IndexerException(indexerResponse, $"Invalid response received from M-Team. Response from API: {jsonResponse.Message}"); + } + return releaseInfos; } @@ -369,11 +384,6 @@ public class MTeamTpParser : IParseIndexerResponse MinimumSeedTime = 172800 // 2 days }; - if (torrent.Imdb.IsNotNullOrWhiteSpace()) - { - release.ImdbId = ParseUtil.GetImdbId(torrent.Imdb.Split('/').LastOrDefault()).GetValueOrDefault(); - } - if (torrent.Status?.CreatedDate != null && DateTime.TryParseExact($"{torrent.Status.CreatedDate} +08:00", "yyyy-MM-dd HH:mm:ss zzz", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var publishDate)) { @@ -390,7 +400,7 @@ public class MTeamTpParser : IParseIndexerResponse private string GetDownloadUrl(int torrentId) { - var url = new HttpUri(_settings.BaseUrl) + var url = new HttpUri(_apiUrl) .CombinePath("/api/torrent/genDlToken") .AddQueryParam("id", torrentId); @@ -455,6 +465,7 @@ internal class MTeamTpApiSearchQuery internal class MTeamTpApiResponse { public MTeamTpApiData Data { get; set; } + public string Message { get; set; } } internal class MTeamTpApiData diff --git a/src/NzbDrone.Core/Indexers/Definitions/MyAnonamouse.cs b/src/NzbDrone.Core/Indexers/Definitions/MyAnonamouse.cs index e4954ec76..dad8fe8e8 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/MyAnonamouse.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/MyAnonamouse.cs @@ -7,6 +7,7 @@ using System.Net; using System.Text.RegularExpressions; using System.Threading.Tasks; using FluentValidation; +using FluentValidation.Results; using Newtonsoft.Json; using NLog; using NzbDrone.Common.Cache; @@ -15,12 +16,14 @@ using NzbDrone.Common.Http; using NzbDrone.Common.Serializer; using NzbDrone.Core.Annotations; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Exceptions; using NzbDrone.Core.Indexers.Exceptions; using NzbDrone.Core.Indexers.Settings; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; namespace NzbDrone.Core.Indexers.Definitions @@ -34,8 +37,8 @@ namespace NzbDrone.Core.Indexers.Definitions public override bool SupportsPagination => true; public override int PageSize => 100; public override IndexerCapabilities Capabilities => SetCapabilities(); + private readonly ICacheManager _cacheManager; - private static readonly Regex TorrentIdRegex = new Regex(@"tor/download.php\?tid=(?<id>\d+)$"); public MyAnonamouse(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger, ICacheManager cacheManager) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) @@ -45,58 +48,103 @@ namespace NzbDrone.Core.Indexers.Definitions public override IIndexerRequestGenerator GetRequestGenerator() { - return new MyAnonamouseRequestGenerator { Settings = Settings, Capabilities = Capabilities }; + return new MyAnonamouseRequestGenerator(Settings, Capabilities, _logger); } public override IParseIndexerResponse GetParser() { - return new MyAnonamouseParser(Settings, Capabilities.Categories, _httpClient, _cacheManager, _logger); + return new MyAnonamouseParser(Definition, Settings, Capabilities.Categories, _httpClient, _cacheManager, _logger); } - public override async Task<byte[]> Download(Uri link) + public override async Task<IndexerDownloadResponse> Download(Uri link) { - if (Settings.Freeleech) - { - _logger.Debug($"Attempting to use freeleech token for {link.AbsoluteUri}"); + var downloadLink = link.RemoveQueryParam("canUseToken"); - var idMatch = TorrentIdRegex.Match(link.AbsoluteUri); - if (idMatch.Success) + if (Settings.UseFreeleechWedge is (int)MyAnonamouseFreeleechWedgeAction.Preferred or (int)MyAnonamouseFreeleechWedgeAction.Required && + bool.TryParse(link.GetQueryParam("canUseToken"), out var canUseToken) && canUseToken) + { + _logger.Debug("Attempting to use freeleech wedge for {0}", downloadLink.AbsoluteUri); + + if (int.TryParse(link.GetQueryParam("tid"), out var torrentId) && torrentId > 0) { - var id = int.Parse(idMatch.Groups["id"].Value); var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); var freeleechUrl = Settings.BaseUrl + $"json/bonusBuy.php/{timestamp}"; - var freeleechRequest = new HttpRequestBuilder(freeleechUrl) + var freeleechRequestBuilder = new HttpRequestBuilder(freeleechUrl) + .Accept(HttpAccept.Json) .AddQueryParam("spendtype", "personalFL") - .AddQueryParam("torrentid", id) - .AddQueryParam("timestamp", timestamp.ToString()) - .Build(); + .AddQueryParam("torrentid", torrentId) + .AddQueryParam("timestamp", timestamp.ToString()); - var indexerReq = new IndexerRequest(freeleechRequest); - var response = await FetchIndexerResponse(indexerReq).ConfigureAwait(false); - var resource = Json.Deserialize<MyAnonamouseBuyPersonalFreeleechResponse>(response.Content); + freeleechRequestBuilder.LogResponseContent = true; + + var cookies = GetCookies(); + + if (cookies != null && cookies.Any()) + { + freeleechRequestBuilder.SetCookies(cookies); + } + + var freeleechRequest = freeleechRequestBuilder.Build(); + + var freeleechResponse = await _httpClient.ExecuteProxiedAsync(freeleechRequest, Definition).ConfigureAwait(false); + + var resource = Json.Deserialize<MyAnonamouseBuyPersonalFreeleechResponse>(freeleechResponse.Content); if (resource.Success) { - _logger.Debug($"Successfully to used freeleech token for torrentid ${id}"); + _logger.Debug("Successfully used freeleech wedge for torrentid {0}.", torrentId); + } + else if (resource.Error.IsNotNullOrWhiteSpace() && resource.Error.ContainsIgnoreCase("This Torrent is VIP")) + { + _logger.Debug("{0} is already VIP, continuing downloading: {1}", torrentId, resource.Error); + } + else if (resource.Error.IsNotNullOrWhiteSpace() && resource.Error.ContainsIgnoreCase("This is already a personal freeleech")) + { + _logger.Debug("{0} is already a personal freeleech, continuing downloading: {1}", torrentId, resource.Error); } else { - _logger.Debug($"Failed to use freeleech token: ${resource.Error}"); + _logger.Warn("Failed to purchase freeleech wedge for {0}: {1}", torrentId, resource.Error); + + if (Settings.UseFreeleechWedge == (int)MyAnonamouseFreeleechWedgeAction.Preferred) + { + _logger.Debug("'Use Freeleech Wedge' option set to preferred, continuing downloading: '{0}'", downloadLink.AbsoluteUri); + } + else + { + throw new ReleaseUnavailableException($"Failed to buy freeleech wedge and 'Use Freeleech Wedge' is set to required, aborting download: '{downloadLink.AbsoluteUri}'"); + } } } else { - _logger.Debug($"Could not get torrent id from link ${link.AbsoluteUri}, skipping freeleech"); + _logger.Warn("Could not get torrent id from link {0}, skipping use of freeleech wedge.", downloadLink.AbsoluteUri); } } - return await base.Download(link).ConfigureAwait(false); + return await base.Download(downloadLink).ConfigureAwait(false); } protected override IDictionary<string, string> GetCookies() { - return CookieUtil.CookieHeaderToDictionary("mam_id=" + Settings.MamId); + var cookies = base.GetCookies(); + + if (cookies is { Count: > 0 } && cookies.TryGetValue("mam_id", out var mamId) && mamId.IsNotNullOrWhiteSpace()) + { + return cookies; + } + + return CookieUtil.CookieHeaderToDictionary($"mam_id={Settings.MamId}"); + } + + protected override async Task Test(List<ValidationFailure> failures) + { + UpdateCookies(null, null); + + _logger.Debug("Cookies cleared."); + + await base.Test(failures).ConfigureAwait(false); } private IndexerCapabilities SetCapabilities() @@ -210,14 +258,31 @@ namespace NzbDrone.Core.Indexers.Definitions public class MyAnonamouseRequestGenerator : IIndexerRequestGenerator { - public MyAnonamouseSettings Settings { get; set; } - public IndexerCapabilities Capabilities { get; set; } + private static readonly Regex SanitizeSearchQueryRegex = new ("[^\\w]+", RegexOptions.IgnoreCase | RegexOptions.Compiled); + + private readonly MyAnonamouseSettings _settings; + private readonly IndexerCapabilities _capabilities; + private readonly Logger _logger; + + public MyAnonamouseRequestGenerator(MyAnonamouseSettings settings, IndexerCapabilities capabilities, Logger logger) + { + _settings = settings; + _capabilities = capabilities; + _logger = logger; + } private IEnumerable<IndexerRequest> GetPagedRequests(SearchCriteriaBase searchCriteria) { - var term = searchCriteria.SanitizedSearchTerm.Trim(); + var term = SanitizeSearchQueryRegex.Replace(searchCriteria.SanitizedSearchTerm, " ").Trim(); - var searchType = Settings.SearchType switch + if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace() && term.IsNullOrWhiteSpace()) + { + _logger.Debug("Search term is empty after being sanitized, stopping search. Initial search term: '{0}'", searchCriteria.SearchTerm); + + yield break; + } + + var searchType = _settings.SearchType switch { (int)MyAnonamouseSearchType.Active => "active", (int)MyAnonamouseSearchType.Freeleech => "fl", @@ -242,29 +307,36 @@ namespace NzbDrone.Core.Indexers.Definitions { "description", "1" } // include the description }; - if (Settings.SearchInDescription) + if (_settings.SearchInDescription) { parameters.Set("tor[srchIn][description]", "true"); } - if (Settings.SearchInSeries) + if (_settings.SearchInSeries) { parameters.Set("tor[srchIn][series]", "true"); } - if (Settings.SearchInFilenames) + if (_settings.SearchInFilenames) { parameters.Set("tor[srchIn][filenames]", "true"); } - var catList = Capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories); + if (_settings.SearchLanguages.Any()) + { + foreach (var (language, index) in _settings.SearchLanguages.Select((value, index) => (value, index))) + { + parameters.Set($"tor[browse_lang][{index}]", language.ToString()); + } + } + + var catList = _capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories).Distinct().ToList(); + if (catList.Any()) { - var index = 0; - foreach (var cat in catList) + foreach (var (category, index) in catList.Select((value, index) => (value, index))) { - parameters.Set("tor[cat][" + index + "]", cat); - index++; + parameters.Set($"tor[cat][{index}]", category); } } else @@ -287,7 +359,7 @@ namespace NzbDrone.Core.Indexers.Definitions parameters.Set("tor[unit]", "1"); } - var searchUrl = Settings.BaseUrl + "tor/js/loadSearchJSONbasic.php"; + var searchUrl = _settings.BaseUrl + "tor/js/loadSearchJSONbasic.php"; if (parameters.Count > 0) { @@ -338,66 +410,78 @@ namespace NzbDrone.Core.Indexers.Definitions public class MyAnonamouseParser : IParseIndexerResponse { + private readonly ProviderDefinition _definition; private readonly MyAnonamouseSettings _settings; private readonly IndexerCapabilitiesCategories _categories; private readonly IIndexerHttpClient _httpClient; private readonly Logger _logger; + private readonly ICached<string> _userClassCache; private readonly HashSet<string> _vipFreeleechUserClasses = new (StringComparer.OrdinalIgnoreCase) { "VIP", - "Elite VIP", + "Elite VIP" }; - public MyAnonamouseParser(MyAnonamouseSettings settings, + public MyAnonamouseParser(ProviderDefinition definition, + MyAnonamouseSettings settings, IndexerCapabilitiesCategories categories, IIndexerHttpClient httpClient, ICacheManager cacheManager, Logger logger) { + _definition = definition; _settings = settings; _categories = categories; _httpClient = httpClient; _logger = logger; + _userClassCache = cacheManager.GetCache<string>(GetType()); } public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse) { + var httpResponse = indexerResponse.HttpResponse; + // Throw auth errors here before we try to parse - if (indexerResponse.HttpResponse.StatusCode == HttpStatusCode.Forbidden) + if (httpResponse.StatusCode == HttpStatusCode.Forbidden) { throw new IndexerAuthException("[403 Forbidden] - mam_id expired or invalid"); } // Throw common http errors here before we try to parse - if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK) + if (httpResponse.StatusCode != HttpStatusCode.OK) { // Remove cookie cache CookiesUpdater(null, null); - throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request"); + throw new IndexerException(indexerResponse, $"Unexpected response status {httpResponse.StatusCode} code from indexer request"); } - if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value)) + if (!httpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value)) { // Remove cookie cache CookiesUpdater(null, null); - throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from indexer request, expected {HttpAccept.Json.Value}"); + throw new IndexerException(indexerResponse, $"Unexpected response header {httpResponse.Headers.ContentType} from indexer request, expected {HttpAccept.Json.Value}"); } - var torrentInfos = new List<TorrentInfo>(); + var releaseInfos = new List<ReleaseInfo>(); var jsonResponse = JsonConvert.DeserializeObject<MyAnonamouseResponse>(indexerResponse.Content); var error = jsonResponse.Error; - if (error is "Nothing returned, out of 0" or "Nothing returned, out of 1") + if (error.IsNotNullOrWhiteSpace() && error.StartsWithIgnoreCase("Nothing returned, out of")) { - return torrentInfos.ToArray(); + return releaseInfos.ToArray(); } - var hasUserVip = HasUserVip(); + if (jsonResponse.Data == null) + { + throw new IndexerException(indexerResponse, "Unexpected response content from indexer request: {0}", jsonResponse.Message ?? "Check the logs for more information."); + } + + var hasUserVip = HasUserVip(httpResponse.GetCookies()); foreach (var item in jsonResponse.Data) { @@ -447,8 +531,10 @@ namespace NzbDrone.Core.Indexers.Definitions release.Title += " [VIP]"; } - release.DownloadUrl = _settings.BaseUrl + "tor/download.php?tid=" + id; - release.InfoUrl = _settings.BaseUrl + "t/" + id; + var isFreeLeech = item.Free || item.PersonalFreeLeech || (hasUserVip && item.FreeVip); + + release.DownloadUrl = GetDownloadUrl(id, !isFreeLeech); + release.InfoUrl = $"{_settings.BaseUrl}t/{id}"; release.Guid = release.InfoUrl; release.Categories = _categories.MapTrackerCatToNewznab(item.Category); release.PublishDate = DateTime.ParseExact(item.Added, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime(); @@ -457,18 +543,35 @@ namespace NzbDrone.Core.Indexers.Definitions release.Seeders = item.Seeders; release.Peers = item.Leechers + release.Seeders; release.Size = ParseUtil.GetBytes(item.Size); - release.DownloadVolumeFactor = item.Free ? 0 : hasUserVip && item.FreeVip ? 0 : 1; + release.DownloadVolumeFactor = isFreeLeech ? 0 : 1; release.UploadVolumeFactor = 1; release.MinimumRatio = 1; release.MinimumSeedTime = 259200; // 72 hours - torrentInfos.Add(release); + releaseInfos.Add(release); } - return torrentInfos.ToArray(); + // Update cookies with the updated mam_id value received in the response + CookiesUpdater(httpResponse.GetCookies(), DateTime.Now.AddDays(30)); + + return releaseInfos.ToArray(); } - private bool HasUserVip() + private string GetDownloadUrl(int torrentId, bool canUseToken) + { + var url = new HttpUri(_settings.BaseUrl) + .CombinePath("/tor/download.php") + .AddQueryParam("tid", torrentId); + + if (_settings.UseFreeleechWedge is (int)MyAnonamouseFreeleechWedgeAction.Preferred or (int)MyAnonamouseFreeleechWedgeAction.Required && canUseToken) + { + url = url.AddQueryParam("canUseToken", "true"); + } + + return url.FullUri; + } + + private bool HasUserVip(Dictionary<string, string> cookies) { var cacheKey = "myanonamouse_user_class_" + _settings.ToJson().SHA256Hash(); @@ -479,15 +582,17 @@ namespace NzbDrone.Core.Indexers.Definitions var request = new HttpRequestBuilder(_settings.BaseUrl.Trim('/')) .Resource("/jsonLoad.php") .Accept(HttpAccept.Json) + .SetCookies(cookies) .Build(); - _logger.Debug("Fetching user data: " + request.Url.FullUri); + _logger.Debug("Fetching user data: {0}", request.Url.FullUri); - request.Cookies.Add("mam_id", _settings.MamId); + var response = _httpClient.ExecuteProxied(request, _definition); - var response = _httpClient.Get(request); var jsonResponse = JsonConvert.DeserializeObject<MyAnonamouseUserDataResponse>(response.Content); + _logger.Trace("Current user class: '{0}'", jsonResponse.UserClass); + return jsonResponse.UserClass?.Trim(); }, TimeSpan.FromHours(1)); @@ -517,6 +622,8 @@ namespace NzbDrone.Core.Indexers.Definitions SearchInDescription = false; SearchInSeries = false; SearchInFilenames = false; + SearchLanguages = Array.Empty<int>(); + UseFreeleechWedge = (int)MyAnonamouseFreeleechWedgeAction.Never; } [FieldDefinition(2, Type = FieldType.Textbox, Label = "Mam Id", HelpText = "Mam Session Id (Created Under Preferences -> Security)")] @@ -525,18 +632,21 @@ namespace NzbDrone.Core.Indexers.Definitions [FieldDefinition(3, Type = FieldType.Select, Label = "Search Type", SelectOptions = typeof(MyAnonamouseSearchType), HelpText = "Specify the desired search type")] public int SearchType { get; set; } - [FieldDefinition(4, Type = FieldType.Checkbox, Label = "Use Freeleech Wedges", HelpText = "Use freeleech wedges to make grabbed torrents personal freeleech")] - public bool Freeleech { get; set; } - - [FieldDefinition(5, Type = FieldType.Checkbox, Label = "Search in description", HelpText = "Search text in the description")] + [FieldDefinition(4, Type = FieldType.Checkbox, Label = "Search in description", HelpText = "Search text in the description")] public bool SearchInDescription { get; set; } - [FieldDefinition(6, Type = FieldType.Checkbox, Label = "Search in series", HelpText = "Search text in the series")] + [FieldDefinition(5, Type = FieldType.Checkbox, Label = "Search in series", HelpText = "Search text in the series")] public bool SearchInSeries { get; set; } - [FieldDefinition(7, Type = FieldType.Checkbox, Label = "Search in filenames", HelpText = "Search text in the filenames")] + [FieldDefinition(6, Type = FieldType.Checkbox, Label = "Search in filenames", HelpText = "Search text in the filenames")] public bool SearchInFilenames { get; set; } + [FieldDefinition(7, Type = FieldType.Select, Label = "Search Languages", SelectOptions = typeof(MyAnonamouseSearchLanguages), HelpText = "Specify the desired languages. If unspecified, all options are used.")] + public IEnumerable<int> SearchLanguages { get; set; } + + [FieldDefinition(8, Type = FieldType.Select, Label = "Use Freeleech Wedges", SelectOptions = typeof(MyAnonamouseFreeleechWedgeAction), HelpText = "Use freeleech wedges to make grabbed torrents personal freeleech")] + public int UseFreeleechWedge { get; set; } + public override NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); @@ -547,18 +657,227 @@ namespace NzbDrone.Core.Indexers.Definitions { [FieldOption(Label="All torrents", Hint = "Search everything")] All = 0, + [FieldOption(Label="Only active", Hint = "Last update had 1+ seeders")] Active = 1, + [FieldOption(Label="Freeleech", Hint = "Freeleech torrents")] Freeleech = 2, + [FieldOption(Label="Freeleech or VIP", Hint = "Freeleech or VIP torrents")] FreeleechOrVip = 3, + [FieldOption(Label="VIP", Hint = "VIP torrents")] Vip = 4, + [FieldOption(Label="Not VIP", Hint = "Torrents not VIP")] NotVip = 5, } + public enum MyAnonamouseSearchLanguages + { + [FieldOption(Label="English")] + English = 1, + + [FieldOption(Label="Afrikaans")] + Afrikaans = 17, + + [FieldOption(Label="Arabic")] + Arabic = 32, + + [FieldOption(Label="Bengali")] + Bengali = 35, + + [FieldOption(Label="Bosnian")] + Bosnian = 51, + + [FieldOption(Label="Bulgarian")] + Bulgarian = 18, + + [FieldOption(Label="Burmese")] + Burmese = 6, + + [FieldOption(Label="Cantonese")] + Cantonese = 44, + + [FieldOption(Label="Catalan")] + Catalan = 19, + + [FieldOption(Label="Chinese")] + Chinese = 2, + + [FieldOption(Label="Croatian")] + Croatian = 49, + + [FieldOption(Label="Czech")] + Czech = 20, + + [FieldOption(Label="Danish")] + Danish = 21, + + [FieldOption(Label="Dutch")] + Dutch = 22, + + [FieldOption(Label="Estonian")] + Estonian = 61, + + [FieldOption(Label="Farsi")] + Farsi = 39, + + [FieldOption(Label="Finnish")] + Finnish = 23, + + [FieldOption(Label="French")] + French = 36, + + [FieldOption(Label="German")] + German = 37, + + [FieldOption(Label="Greek")] + Greek = 26, + + [FieldOption(Label="Greek, Ancient")] + GreekAncient = 59, + + [FieldOption(Label="Gujarati")] + Gujarati = 3, + + [FieldOption(Label="Hebrew")] + Hebrew = 27, + + [FieldOption(Label="Hindi")] + Hindi = 8, + + [FieldOption(Label="Hungarian")] + Hungarian = 28, + + [FieldOption(Label="Icelandic")] + Icelandic = 63, + + [FieldOption(Label="Indonesian")] + Indonesian = 53, + + [FieldOption(Label="Irish")] + Irish = 56, + + [FieldOption(Label="Italian")] + Italian = 43, + + [FieldOption(Label="Japanese")] + Japanese = 38, + + [FieldOption(Label="Javanese")] + Javanese = 12, + + [FieldOption(Label="Kannada")] + Kannada = 5, + + [FieldOption(Label="Korean")] + Korean = 41, + + [FieldOption(Label="Lithuanian")] + Lithuanian = 50, + + [FieldOption(Label="Latin")] + Latin = 46, + + [FieldOption(Label="Latvian")] + Latvian = 62, + + [FieldOption(Label="Malay")] + Malay = 33, + + [FieldOption(Label="Malayalam")] + Malayalam = 58, + + [FieldOption(Label="Manx")] + Manx = 57, + + [FieldOption(Label="Marathi")] + Marathi = 9, + + [FieldOption(Label="Norwegian")] + Norwegian = 48, + + [FieldOption(Label="Polish")] + Polish = 45, + + [FieldOption(Label="Portuguese")] + Portuguese = 34, + + [FieldOption(Label="Brazilian Portuguese (BP)")] + BrazilianPortuguese = 52, + + [FieldOption(Label="Punjabi")] + Punjabi = 14, + + [FieldOption(Label="Romanian")] + Romanian = 30, + + [FieldOption(Label="Russian")] + Russian = 16, + + [FieldOption(Label="Scottish Gaelic")] + ScottishGaelic = 24, + + [FieldOption(Label="Sanskrit")] + Sanskrit = 60, + + [FieldOption(Label="Serbian")] + Serbian = 31, + + [FieldOption(Label="Slovenian")] + Slovenian = 54, + + [FieldOption(Label="Spanish")] + Spanish = 4, + + [FieldOption(Label="Castilian Spanish")] + CastilianSpanish = 55, + + [FieldOption(Label="Swedish")] + Swedish = 40, + + [FieldOption(Label="Tagalog")] + Tagalog = 29, + + [FieldOption(Label="Tamil")] + Tamil = 11, + + [FieldOption(Label="Telugu")] + Telugu = 10, + + [FieldOption(Label="Thai")] + Thai = 7, + + [FieldOption(Label="Turkish")] + Turkish = 42, + + [FieldOption(Label="Ukrainian")] + Ukrainian = 25, + + [FieldOption(Label="Urdu")] + Urdu = 15, + + [FieldOption(Label="Vietnamese")] + Vietnamese = 13, + + [FieldOption(Label="Other")] + Other = 47, + } + + public enum MyAnonamouseFreeleechWedgeAction + { + [FieldOption(Label = "Never", Hint = "Do not buy as freeleech")] + Never = 0, + + [FieldOption(Label = "Preferred", Hint = "Buy and use wedge if possible")] + Preferred = 1, + + [FieldOption(Label = "Required", Hint = "Abort download if unable to buy wedge")] + Required = 2, + } + public class MyAnonamouseTorrent { public int Id { get; set; } @@ -571,6 +890,8 @@ namespace NzbDrone.Core.Indexers.Definitions public string Filetype { get; set; } public bool Vip { get; set; } public bool Free { get; set; } + [JsonProperty(PropertyName = "personal_freeleech")] + public bool PersonalFreeLeech { get; set; } [JsonProperty(PropertyName = "fl_vip")] public bool FreeVip { get; set; } public string Category { get; set; } @@ -586,7 +907,8 @@ namespace NzbDrone.Core.Indexers.Definitions public class MyAnonamouseResponse { public string Error { get; set; } - public List<MyAnonamouseTorrent> Data { get; set; } + public IReadOnlyCollection<MyAnonamouseTorrent> Data { get; set; } + public string Message { get; set; } } public class MyAnonamouseBuyPersonalFreeleechResponse @@ -597,7 +919,7 @@ namespace NzbDrone.Core.Indexers.Definitions public class MyAnonamouseUserDataResponse { - [JsonProperty(PropertyName = "class")] + [JsonProperty(PropertyName = "classname")] public string UserClass { get; set; } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Nebulance.cs b/src/NzbDrone.Core/Indexers/Definitions/Nebulance.cs index 36247522f..dcc26338e 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Nebulance.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Nebulance.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Net; using System.Text; using System.Text.Json.Serialization; -using System.Text.RegularExpressions; using System.Threading.Tasks; using Newtonsoft.Json; using NLog; @@ -42,7 +41,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IIndexerRequestGenerator GetRequestGenerator() { - return new NebulanceRequestGenerator(Settings); + return new NebulanceRequestGenerator(Settings, _logger); } public override IParseIndexerResponse GetParser() @@ -68,26 +67,6 @@ namespace NzbDrone.Core.Indexers.Definitions return Task.FromResult(request); } - protected override IList<ReleaseInfo> CleanupReleases(IEnumerable<ReleaseInfo> releases, SearchCriteriaBase searchCriteria) - { - var cleanReleases = base.CleanupReleases(releases, searchCriteria); - - return FilterReleasesByQuery(cleanReleases, searchCriteria).ToList(); - } - - protected override IEnumerable<ReleaseInfo> FilterReleasesByQuery(IEnumerable<ReleaseInfo> releases, SearchCriteriaBase searchCriteria) - { - if (!searchCriteria.IsRssSearch && - searchCriteria.IsIdSearch && - searchCriteria is TvSearchCriteria tvSearchCriteria && - tvSearchCriteria.EpisodeSearchString.IsNotNullOrWhiteSpace()) - { - releases = releases.Where(r => r.Title.IsNotNullOrWhiteSpace() && r.Title.ContainsIgnoreCase(tvSearchCriteria.EpisodeSearchString)).ToList(); - } - - return releases; - } - private IndexerCapabilities SetCapabilities() { var caps = new IndexerCapabilities @@ -111,10 +90,12 @@ namespace NzbDrone.Core.Indexers.Definitions public class NebulanceRequestGenerator : IIndexerRequestGenerator { private readonly NebulanceSettings _settings; + private readonly Logger _logger; - public NebulanceRequestGenerator(NebulanceSettings settings) + public NebulanceRequestGenerator(NebulanceSettings settings, Logger logger) { _settings = settings; + _logger = logger; } public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) @@ -136,40 +117,66 @@ namespace NzbDrone.Core.Indexers.Definitions Age = ">0" }; - if (searchCriteria.SanitizedTvSearchString.IsNotNullOrWhiteSpace()) + if (searchCriteria.TvMazeId is > 0) { - queryParams.Name = "%" + Regex.Replace(searchCriteria.SanitizedTvSearchString, "[\\W]+", "%").Trim() + "%"; + queryParams.TvMaze = searchCriteria.TvMazeId.Value; + } + else if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace()) + { + queryParams.Imdb = searchCriteria.FullImdbId; } - if (searchCriteria.TvMazeId.HasValue) - { - queryParams.Tvmaze = searchCriteria.TvMazeId.Value; + var searchQuery = searchCriteria.SanitizedSearchTerm.Trim(); - if (searchCriteria.EpisodeSearchString.IsNotNullOrWhiteSpace()) + if (searchQuery.IsNotNullOrWhiteSpace()) + { + queryParams.Release = searchQuery; + } + + if (searchCriteria.Season.HasValue && + searchCriteria.Episode.IsNotNullOrWhiteSpace() && + DateTime.TryParseExact($"{searchCriteria.Season} {searchCriteria.Episode}", "yyyy MM/dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var showDate)) + { + if (searchQuery.IsNotNullOrWhiteSpace()) { - queryParams.Name = "%" + Regex.Replace(searchCriteria.EpisodeSearchString, "[\\W]+", "%").Trim() + "%"; + queryParams.Name = searchQuery; + } + + queryParams.Release = showDate.ToString("yyyy.MM.dd", CultureInfo.InvariantCulture); + } + else + { + if (searchCriteria.Season.HasValue) + { + queryParams.Season = searchCriteria.Season.Value; + } + + if (searchCriteria.Episode.IsNotNullOrWhiteSpace() && int.TryParse(searchCriteria.Episode, out var episodeNumber)) + { + queryParams.Episode = episodeNumber; } } - else if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace() && int.TryParse(searchCriteria.ImdbId, out var intImdb)) - { - queryParams.Imdb = intImdb; - if (searchCriteria.EpisodeSearchString.IsNotNullOrWhiteSpace()) - { - queryParams.Name = "%" + Regex.Replace(searchCriteria.EpisodeSearchString, "[\\W]+", "%").Trim() + "%"; - } + if ((queryParams.Season.HasValue || queryParams.Episode.HasValue) && + queryParams.Name.IsNullOrWhiteSpace() && + queryParams.Release.IsNullOrWhiteSpace() && + !queryParams.TvMaze.HasValue && + queryParams.Imdb.IsNullOrWhiteSpace()) + { + _logger.Debug("NBL API does not support season calls without name, series, id, imdb, tvmaze, or time keys."); + + return new IndexerPageableRequestChain(); + } + + if (queryParams.Name is { Length: > 0 and < 3 } || queryParams.Release is { Length: > 0 and < 3 }) + { + _logger.Debug("NBL API does not support release calls that are 2 characters or fewer."); + + return new IndexerPageableRequestChain(); } pageableRequests.Add(GetPagedRequests(queryParams, searchCriteria.Limit, searchCriteria.Offset)); - if (queryParams.Name.IsNotNullOrWhiteSpace() && (queryParams.Tvmaze is > 0 || queryParams.Imdb is > 0)) - { - queryParams = queryParams.Clone(); - queryParams.Name = null; - - pageableRequests.Add(GetPagedRequests(queryParams, searchCriteria.Limit, searchCriteria.Offset)); - } - return pageableRequests; } @@ -187,9 +194,18 @@ namespace NzbDrone.Core.Indexers.Definitions Age = ">0" }; - if (searchCriteria.SanitizedSearchTerm.IsNotNullOrWhiteSpace()) + var searchQuery = searchCriteria.SanitizedSearchTerm.Trim(); + + if (searchQuery.IsNotNullOrWhiteSpace()) { - queryParams.Name = "%" + Regex.Replace(searchCriteria.SanitizedSearchTerm, "[\\W]+", "%").Trim() + "%"; + queryParams.Release = searchQuery; + } + + if (queryParams.Release is { Length: > 0 and < 3 }) + { + _logger.Debug("NBL API does not support release calls that are 2 characters or fewer."); + + return new IndexerPageableRequestChain(); } pageableRequests.Add(GetPagedRequests(queryParams, searchCriteria.Limit, searchCriteria.Offset)); @@ -228,17 +244,30 @@ namespace NzbDrone.Core.Indexers.Definitions if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK) { - throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request"); + STJson.TryDeserialize<JsonRpcResponse<NebulanceErrorResponse>>(indexerResponse.HttpResponse.Content, out var errorResponse); + + throw new IndexerException(indexerResponse, "Unexpected response status '{0}' code from indexer request: {1}", indexerResponse.HttpResponse.StatusCode, errorResponse?.Result?.Error?.Message ?? "Check the logs for more information."); } - var jsonResponse = STJson.Deserialize<JsonRpcResponse<NebulanceTorrents>>(indexerResponse.HttpResponse.Content); + JsonRpcResponse<NebulanceResponse> jsonResponse; + + try + { + jsonResponse = STJson.Deserialize<JsonRpcResponse<NebulanceResponse>>(indexerResponse.HttpResponse.Content); + } + catch (Exception ex) + { + STJson.TryDeserialize<JsonRpcResponse<string>>(indexerResponse.HttpResponse.Content, out var response); + + throw new IndexerException(indexerResponse, "Unexpected response from indexer request: {0}", ex, response?.Result ?? ex.Message); + } if (jsonResponse.Error != null || jsonResponse.Result == null) { throw new IndexerException(indexerResponse, "Indexer API call returned an error [{0}]", jsonResponse.Error); } - if (jsonResponse.Result.Items.Count == 0) + if (jsonResponse.Result?.Items == null || jsonResponse.Result.Items.Count == 0) { return torrentInfos; } @@ -253,14 +282,13 @@ namespace NzbDrone.Core.Indexers.Definitions var release = new TorrentInfo { - Title = title, Guid = details, InfoUrl = details, - PosterUrl = row.Banner, DownloadUrl = row.Download, + Title = title.Trim(), Categories = new List<IndexerCategory> { TvCategoryFromQualityParser.ParseTvShowQuality(row.ReleaseTitle) }, Size = ParseUtil.CoerceLong(row.Size), - Files = row.FileList.Length, + Files = row.FileList.Count(), PublishDate = DateTime.Parse(row.PublishDateUtc, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal), Grabs = ParseUtil.CoerceInt(row.Snatch), Seeders = ParseUtil.CoerceInt(row.Seed), @@ -269,7 +297,8 @@ namespace NzbDrone.Core.Indexers.Definitions MinimumRatio = 0, // ratioless MinimumSeedTime = row.Category.ToLower() == "season" ? 432000 : 86400, // 120 hours for seasons and 24 hours for episodes DownloadVolumeFactor = 0, // ratioless tracker - UploadVolumeFactor = 1 + UploadVolumeFactor = 1, + PosterUrl = row.Banner }; if (row.TvMazeId.IsNotNullOrWhiteSpace()) @@ -293,7 +322,7 @@ namespace NzbDrone.Core.Indexers.Definitions ApiKey = ""; } - [FieldDefinition(4, Label = "ApiKey", HelpText = "IndexerNebulanceSettingsApiKeyHelpText")] + [FieldDefinition(2, Label = "ApiKey", HelpText = "IndexerNebulanceSettingsApiKeyHelpText", Privacy = PrivacyLevel.ApiKey)] public string ApiKey { get; set; } } @@ -301,60 +330,96 @@ namespace NzbDrone.Core.Indexers.Definitions { [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public string Id { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public string Time { get; set; } + [JsonProperty(PropertyName="age", DefaultValueHandling = DefaultValueHandling.Ignore)] public string Age { get; set; } + [JsonProperty(PropertyName="tvmaze", DefaultValueHandling = DefaultValueHandling.Ignore)] - public int? Tvmaze { get; set; } + public int? TvMaze { get; set; } + [JsonProperty(PropertyName="imdb", DefaultValueHandling = DefaultValueHandling.Ignore)] - public int? Imdb { get; set; } + public string Imdb { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public string Hash { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public string[] Tags { get; set; } + [JsonProperty(PropertyName="name", DefaultValueHandling = DefaultValueHandling.Ignore)] public string Name { get; set; } + + [JsonProperty(PropertyName="release", DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Release { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public string Category { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public string Series { get; set; } + [JsonProperty(PropertyName="season", DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Season { get; set; } + + [JsonProperty(PropertyName="episode", DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Episode { get; set; } + public NebulanceQuery Clone() { return MemberwiseClone() as NebulanceQuery; } } + public class NebulanceResponse + { + public List<NebulanceTorrent> Items { get; set; } + } + public class NebulanceTorrent { [JsonPropertyName("rls_name")] public string ReleaseTitle { get; set; } + [JsonPropertyName("cat")] public string Category { get; set; } + public string Size { get; set; } public string Seed { get; set; } public string Leech { get; set; } public string Snatch { get; set; } public string Download { get; set; } + [JsonPropertyName("file_list")] - public string[] FileList { get; set; } + public IEnumerable<string> FileList { get; set; } = Array.Empty<string>(); + [JsonPropertyName("group_name")] public string GroupName { get; set; } + [JsonPropertyName("series_banner")] public string Banner { get; set; } + [JsonPropertyName("group_id")] public string TorrentId { get; set; } + [JsonPropertyName("series_id")] public string TvMazeId { get; set; } + [JsonPropertyName("rls_utc")] public string PublishDateUtc { get; set; } - public IEnumerable<string> Tags { get; set; } + + public IEnumerable<string> Tags { get; set; } = Array.Empty<string>(); } - public class NebulanceTorrents + public class NebulanceErrorResponse { - public List<NebulanceTorrent> Items { get; set; } - public int Results { get; set; } + public NebulanceErrorMessage Error { get; set; } + } + + public class NebulanceErrorMessage + { + public string Message { get; set; } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabRequestGenerator.cs index f9ab56128..ad3f48b07 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabRequestGenerator.cs @@ -128,7 +128,7 @@ namespace NzbDrone.Core.Indexers.Newznab parameters.Set("tvdbid", searchCriteria.TvdbId.Value.ToString()); } - if (searchCriteria.TmdbId.HasValue && capabilities.TvSearchTvdbAvailable) + if (searchCriteria.TmdbId.HasValue && capabilities.TvSearchTmdbAvailable) { parameters.Set("tmdbid", searchCriteria.TmdbId.Value.ToString()); } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabRssParser.cs b/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabRssParser.cs index b1327a15a..751850af1 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabRssParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabRssParser.cs @@ -74,16 +74,17 @@ namespace NzbDrone.Core.Indexers.Newznab protected override bool PostProcess(IndexerResponse indexerResponse, List<XElement> items, List<ReleaseInfo> releases) { var enclosureTypes = items.SelectMany(GetEnclosures).Select(v => v.Type).Distinct().ToArray(); + if (enclosureTypes.Any() && enclosureTypes.Intersect(PreferredEnclosureMimeTypes).Empty()) { if (enclosureTypes.Intersect(TorrentEnclosureMimeTypes).Any()) { - _logger.Warn("Feed does not contain {0}, found {1}, did you intend to add a Torznab indexer?", NzbEnclosureMimeType, enclosureTypes[0]); - } - else - { - _logger.Warn("Feed does not contain {0}, found {1}.", NzbEnclosureMimeType, enclosureTypes[0]); + _logger.Warn("{0} does not contain {1}, found {2}, did you intend to add a Torznab indexer?", indexerResponse.Request.Url, NzbEnclosureMimeType, enclosureTypes[0]); + + return false; } + + _logger.Warn("{0} does not contain {1}, found {2}.", indexerResponse.Request.Url, NzbEnclosureMimeType, enclosureTypes[0]); } return true; diff --git a/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabSettings.cs index 67b787e12..2ca4362f6 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabSettings.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabSettings.cs @@ -42,7 +42,8 @@ namespace NzbDrone.Core.Indexers.Newznab RuleFor(c => c.VipExpiration).Must(c => c.IsFutureDate()) .When(c => c.VipExpiration.IsNotNullOrWhiteSpace()) - .WithMessage("Must be a future date"); + .WithMessage("Must be a future date") + .AsWarning(); } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/NorBits.cs b/src/NzbDrone.Core/Indexers/Definitions/NorBits.cs index 5022f32d7..e989a5c6a 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/NorBits.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/NorBits.cs @@ -27,7 +27,7 @@ public class NorBits : TorrentIndexerBase<NorBitsSettings> public override string[] IndexerUrls => new[] { "https://norbits.net/" }; public override string Description => "NorBits is a Norwegian Private site for MOVIES / TV / GENERAL"; public override string Language => "nb-NO"; - public override Encoding Encoding => Encoding.GetEncoding("iso-8859-1"); + public override Encoding Encoding => Encoding.UTF8; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); @@ -129,26 +129,14 @@ public class NorBits : TorrentIndexerBase<NorBitsSettings> } }; - caps.Categories.AddCategoryMapping("main_cat[]=1&sub2_cat[]=49", NewznabStandardCategory.MoviesUHD, "Filmer - UHD-2160p"); - caps.Categories.AddCategoryMapping("main_cat[]=1&sub2_cat[]=19", NewznabStandardCategory.MoviesHD, "Filmer - HD-1080p/i"); - caps.Categories.AddCategoryMapping("main_cat[]=1&sub2_cat[]=20", NewznabStandardCategory.MoviesHD, "Filmer - HD-720p"); - caps.Categories.AddCategoryMapping("main_cat[]=1&sub2_cat[]=22", NewznabStandardCategory.MoviesSD, "Filmer - SD"); - caps.Categories.AddCategoryMapping("main_cat[]=2&sub2_cat[]=49", NewznabStandardCategory.TVUHD, "TV - UHD-2160p"); - caps.Categories.AddCategoryMapping("main_cat[]=2&sub2_cat[]=19", NewznabStandardCategory.TVHD, "TV - HD-1080p/i"); - caps.Categories.AddCategoryMapping("main_cat[]=2&sub2_cat[]=20", NewznabStandardCategory.TVHD, "TV - HD-720p"); - caps.Categories.AddCategoryMapping("main_cat[]=2&sub2_cat[]=22", NewznabStandardCategory.TVSD, "TV - SD"); + caps.Categories.AddCategoryMapping("main_cat[]=1", NewznabStandardCategory.Movies, "Filmer"); + caps.Categories.AddCategoryMapping("main_cat[]=2", NewznabStandardCategory.TV, "TV"); caps.Categories.AddCategoryMapping("main_cat[]=3", NewznabStandardCategory.PC, "Programmer"); caps.Categories.AddCategoryMapping("main_cat[]=4", NewznabStandardCategory.Console, "Spill"); - caps.Categories.AddCategoryMapping("main_cat[]=5&sub2_cat[]=42", NewznabStandardCategory.AudioMP3, "Musikk - 192"); - caps.Categories.AddCategoryMapping("main_cat[]=5&sub2_cat[]=43", NewznabStandardCategory.AudioMP3, "Musikk - 256"); - caps.Categories.AddCategoryMapping("main_cat[]=5&sub2_cat[]=44", NewznabStandardCategory.AudioMP3, "Musikk - 320"); - caps.Categories.AddCategoryMapping("main_cat[]=5&sub2_cat[]=45", NewznabStandardCategory.AudioMP3, "Musikk - VBR"); - caps.Categories.AddCategoryMapping("main_cat[]=5&sub2_cat[]=46", NewznabStandardCategory.AudioLossless, "Musikk - Lossless"); + caps.Categories.AddCategoryMapping("main_cat[]=5", NewznabStandardCategory.Audio, "Musikk"); caps.Categories.AddCategoryMapping("main_cat[]=6", NewznabStandardCategory.Books, "Tidsskrift"); caps.Categories.AddCategoryMapping("main_cat[]=7", NewznabStandardCategory.AudioAudiobook, "Lydbøker"); - caps.Categories.AddCategoryMapping("main_cat[]=8&sub2_cat[]=19", NewznabStandardCategory.AudioVideo, "Musikkvideoer - HD-1080p/i"); - caps.Categories.AddCategoryMapping("main_cat[]=8&sub2_cat[]=20", NewznabStandardCategory.AudioVideo, "Musikkvideoer - HD-720p"); - caps.Categories.AddCategoryMapping("main_cat[]=8&sub2_cat[]=22", NewznabStandardCategory.AudioVideo, "Musikkvideoer - SD"); + caps.Categories.AddCategoryMapping("main_cat[]=8", NewznabStandardCategory.AudioVideo, "Musikkvideoer"); caps.Categories.AddCategoryMapping("main_cat[]=40", NewznabStandardCategory.AudioOther, "Podcasts"); return caps; @@ -190,7 +178,7 @@ public class NorBitsRequestGenerator : IIndexerRequestGenerator } else if (!string.IsNullOrWhiteSpace(term)) { - searchTerm = "search=" + term.UrlEncode(Encoding.GetEncoding(28591)); + searchTerm = "search=" + term.UrlEncode(Encoding.UTF8); } searchUrl += "?" + searchTerm + "&" + parameters.GetQueryString(); @@ -277,20 +265,17 @@ public class NorBitsParser : IParseIndexerResponse foreach (var row in rows) { - var link = _settings.BaseUrl + row.QuerySelector("td:nth-of-type(2) > a[href*=\"download.php?id=\"]")?.GetAttribute("href").TrimStart('/'); + var link = _settings.BaseUrl + row.QuerySelector("td:nth-of-type(2) > a[href*=\"download.php?id=\"]")?.GetAttribute("href")?.TrimStart('/'); var qDetails = row.QuerySelector("td:nth-of-type(2) > a[href*=\"details.php?id=\"]"); - var title = qDetails?.GetAttribute("title").Trim(); - var details = _settings.BaseUrl + qDetails?.GetAttribute("href").TrimStart('/'); + var title = qDetails?.GetAttribute("title")?.Trim(); + var details = _settings.BaseUrl + qDetails?.GetAttribute("href")?.TrimStart('/'); - var mainCategory = row.QuerySelector("td:nth-of-type(1) > div > a[href*=\"main_cat[]\"]")?.GetAttribute("href")?.Split('?').Last(); - var secondCategory = row.QuerySelector("td:nth-of-type(1) > div > a[href*=\"sub2_cat[]\"]")?.GetAttribute("href")?.Split('?').Last(); + var catQuery = row.QuerySelector("td:nth-of-type(1) a[href*=\"main_cat[]\"]")?.GetAttribute("href")?.Split('?').Last().Split('&', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + var category = catQuery?.FirstOrDefault(x => x.StartsWith("main_cat[]=", StringComparison.OrdinalIgnoreCase)); - var categoryList = new[] { mainCategory, secondCategory }; - var cat = string.Join("&", categoryList.Where(c => !string.IsNullOrWhiteSpace(c))); - - var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(9)").TextContent); - var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(10)").TextContent); + var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(9)")?.TextContent); + var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(10)")?.TextContent); var release = new TorrentInfo { @@ -298,7 +283,7 @@ public class NorBitsParser : IParseIndexerResponse InfoUrl = details, DownloadUrl = link, Title = title, - Categories = _categories.MapTrackerCatToNewznab(cat), + Categories = _categories.MapTrackerCatToNewznab(category), Size = ParseUtil.GetBytes(row.QuerySelector("td:nth-of-type(7)")?.TextContent), Files = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(3) > a")?.TextContent.Trim()), Grabs = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(8)")?.FirstChild?.TextContent.Trim()), @@ -319,8 +304,8 @@ public class NorBitsParser : IParseIndexerResponse release.Genres = genres.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToList(); } - var imdbLink = row.QuerySelector("a[href*=\"imdb.com/title/tt\"]")?.GetAttribute("href"); - release.ImdbId = ParseUtil.GetImdbId(imdbLink) ?? 0; + var imdbId = ParseUtil.GetImdbId(row.QuerySelector("a[href*=\"imdb.com/title/tt\"]")?.GetAttribute("href")?.TrimEnd('/')?.Split('/')?.LastOrDefault()); + release.ImdbId = imdbId ?? 0; if (row.QuerySelector("img[title=\"100% freeleech\"]") != null) { diff --git a/src/NzbDrone.Core/Indexers/Definitions/NzbIndex.cs b/src/NzbDrone.Core/Indexers/Definitions/NzbIndex.cs index 8fe5f4d04..58c61ef77 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/NzbIndex.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/NzbIndex.cs @@ -20,6 +20,7 @@ using NzbDrone.Core.Validation; namespace NzbDrone.Core.Indexers.Definitions { + [Obsolete("Site has removed API access.")] public class NzbIndex : UsenetIndexerBase<NzbIndexSettings> { public override string Name => "NZBIndex"; diff --git a/src/NzbDrone.Core/Indexers/Definitions/Orpheus.cs b/src/NzbDrone.Core/Indexers/Definitions/Orpheus.cs index 67f8ff55a..7075e170b 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Orpheus.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Orpheus.cs @@ -8,6 +8,7 @@ using FluentValidation; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; +using NzbDrone.Common.Serializer; using NzbDrone.Core.Annotations; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers.Definitions.Gazelle; @@ -50,6 +51,20 @@ namespace NzbDrone.Core.Indexers.Definitions return new OrpheusParser(Settings, Capabilities.Categories); } + protected override Task<HttpRequest> GetDownloadRequest(Uri link) + { + var requestBuilder = new HttpRequestBuilder(link.AbsoluteUri) + { + AllowAutoRedirect = FollowRedirect + }; + + var request = requestBuilder + .SetHeader("Authorization", $"token {Settings.Apikey}") + .Build(); + + return Task.FromResult(request); + } + protected override IList<ReleaseInfo> CleanupReleases(IEnumerable<ReleaseInfo> releases, SearchCriteriaBase searchCriteria) { var cleanReleases = base.CleanupReleases(releases, searchCriteria); @@ -89,46 +104,30 @@ namespace NzbDrone.Core.Indexers.Definitions return caps; } - public override async Task<byte[]> Download(Uri link) + public override async Task<IndexerDownloadResponse> Download(Uri link) { - var request = new HttpRequestBuilder(link.AbsoluteUri) - .SetHeader("Authorization", $"token {Settings.Apikey}") - .Build(); + var downloadResponse = await base.Download(link); - var downloadBytes = Array.Empty<byte>(); + var fileData = downloadResponse.Data; - try + if (Settings.UseFreeleechToken == (int)OrpheusFreeleechTokenAction.Preferred + && fileData.Length >= 1 + && fileData[0] != 'd' // simple test for torrent vs HTML content + && link.Query.Contains("usetoken=1")) { - var response = await _httpClient.ExecuteProxiedAsync(request, Definition); - downloadBytes = response.ResponseData; + var html = Encoding.GetString(fileData); - if (downloadBytes.Length >= 1 - && downloadBytes[0] != 'd' // simple test for torrent vs HTML content - && link.Query.Contains("usetoken=1")) + if (html.Contains("You do not have any freeleech tokens left.") + || html.Contains("You do not have enough freeleech tokens") + || html.Contains("This torrent is too large.") + || html.Contains("You cannot use tokens here")) { - var html = Encoding.GetString(downloadBytes); - if (html.Contains("You do not have any freeleech tokens left.") - || html.Contains("You do not have enough freeleech tokens") - || html.Contains("This torrent is too large.") - || html.Contains("You cannot use tokens here")) - { - // download again without usetoken - request.Url = new HttpUri(link.ToString().Replace("&usetoken=1", "")); - - response = await _httpClient.ExecuteProxiedAsync(request, Definition); - downloadBytes = response.ResponseData; - } + // Try to download again without usetoken + downloadResponse = await base.Download(link.RemoveQueryParam("usetoken")); } } - catch (Exception) - { - _indexerStatusService.RecordFailure(Definition.Id); - _logger.Error("Download failed"); - } - ValidateDownloadData(downloadBytes); - - return downloadBytes; + return downloadResponse; } } @@ -254,7 +253,9 @@ namespace NzbDrone.Core.Indexers.Definitions if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK) { - throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request"); + STJson.TryDeserialize<GazelleErrorResponse>(indexerResponse.Content, out var errorResponse); + + throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request: {errorResponse?.Error ?? "Check the logs for more information."}"); } if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value)) @@ -277,7 +278,7 @@ namespace NzbDrone.Core.Indexers.Definitions foreach (var torrent in result.Torrents) { // skip releases that cannot be used with freeleech tokens when the option is enabled - if (_settings.UseFreeleechToken && !torrent.CanUseToken) + if (_settings.UseFreeleechToken == (int)OrpheusFreeleechTokenAction.Required && !torrent.CanUseToken) { continue; } @@ -286,12 +287,13 @@ namespace NzbDrone.Core.Indexers.Definitions var title = GetTitle(result, torrent); var infoUrl = GetInfoUrl(result.GroupId, id); + var isFreeLeech = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsPersonalFreeLeech; var release = new TorrentInfo { Guid = infoUrl, InfoUrl = infoUrl, - DownloadUrl = GetDownloadUrl(id, !torrent.IsFreeLeech && !torrent.IsNeutralLeech && !torrent.IsPersonalFreeLeech), + DownloadUrl = GetDownloadUrl(id, torrent.CanUseToken && !isFreeLeech), Title = WebUtility.HtmlDecode(title), Artist = WebUtility.HtmlDecode(result.Artist), Album = WebUtility.HtmlDecode(result.GroupName), @@ -305,7 +307,7 @@ namespace NzbDrone.Core.Indexers.Definitions Scene = torrent.Scene, Files = torrent.FileCount, Grabs = torrent.Snatches, - DownloadVolumeFactor = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsPersonalFreeLeech ? 0 : 1, + DownloadVolumeFactor = isFreeLeech ? 0 : 1, UploadVolumeFactor = torrent.IsNeutralLeech ? 0 : 1 }; @@ -327,19 +329,20 @@ namespace NzbDrone.Core.Indexers.Definitions else { // skip releases that cannot be used with freeleech tokens when the option is enabled - if (_settings.UseFreeleechToken && !result.CanUseToken) + if (_settings.UseFreeleechToken == (int)OrpheusFreeleechTokenAction.Required && !result.CanUseToken) { continue; } var id = result.TorrentId; var infoUrl = GetInfoUrl(result.GroupId, id); + var isFreeLeech = result.IsFreeLeech || result.IsNeutralLeech || result.IsPersonalFreeLeech; var release = new TorrentInfo { Guid = infoUrl, InfoUrl = infoUrl, - DownloadUrl = GetDownloadUrl(id, !result.IsFreeLeech && !result.IsNeutralLeech && !result.IsPersonalFreeLeech), + DownloadUrl = GetDownloadUrl(id, result.CanUseToken && !isFreeLeech), Title = WebUtility.HtmlDecode(result.GroupName), Size = long.Parse(result.Size), Seeders = int.Parse(result.Seeders), @@ -347,7 +350,7 @@ namespace NzbDrone.Core.Indexers.Definitions PublishDate = long.TryParse(result.GroupTime, out var num) ? DateTimeOffset.FromUnixTimeSeconds(num).UtcDateTime : DateTimeUtil.FromFuzzyTime(result.GroupTime), Files = result.FileCount, Grabs = result.Snatches, - DownloadVolumeFactor = result.IsFreeLeech || result.IsNeutralLeech || result.IsPersonalFreeLeech ? 0 : 1, + DownloadVolumeFactor = isFreeLeech ? 0 : 1, UploadVolumeFactor = result.IsNeutralLeech ? 0 : 1 }; @@ -413,7 +416,7 @@ namespace NzbDrone.Core.Indexers.Definitions .AddQueryParam("id", torrentId); // Orpheus fails to download if usetoken=0 so we need to only add if we will use one - if (_settings.UseFreeleechToken && canUseToken) + if (_settings.UseFreeleechToken is (int)OrpheusFreeleechTokenAction.Preferred or (int)OrpheusFreeleechTokenAction.Required && canUseToken) { url = url.AddQueryParam("usetoken", "1"); } @@ -447,18 +450,30 @@ namespace NzbDrone.Core.Indexers.Definitions public OrpheusSettings() { Apikey = ""; - UseFreeleechToken = false; + UseFreeleechToken = (int)OrpheusFreeleechTokenAction.Never; } [FieldDefinition(2, Label = "ApiKey", HelpText = "IndexerOrpheusSettingsApiKeyHelpText", Privacy = PrivacyLevel.ApiKey)] public string Apikey { get; set; } - [FieldDefinition(3, Label = "Use Freeleech Tokens", HelpText = "Use freeleech tokens when available", Type = FieldType.Checkbox)] - public bool UseFreeleechToken { get; set; } + [FieldDefinition(3, Type = FieldType.Select, Label = "Use Freeleech Tokens", SelectOptions = typeof(OrpheusFreeleechTokenAction), HelpText = "When to use freeleech tokens")] + public int UseFreeleechToken { get; set; } public override NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); } } + + public enum OrpheusFreeleechTokenAction + { + [FieldOption(Label = "Never", Hint = "Do not use tokens")] + Never = 0, + + [FieldOption(Label = "Preferred", Hint = "Use token if possible")] + Preferred = 1, + + [FieldOption(Label = "Required", Hint = "Abort download if unable to use token")] + Required = 2, + } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcorn.cs b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcorn.cs index 9cb147b48..7426157ab 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcorn.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcorn.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using NLog; using NzbDrone.Core.Configuration; @@ -15,6 +16,7 @@ namespace NzbDrone.Core.Indexers.Definitions.PassThePopcorn public override bool SupportsSearch => true; public override bool SupportsPagination => true; public override int PageSize => 50; + public override TimeSpan RateLimit => TimeSpan.FromSeconds(4); public override IndexerCapabilities Capabilities => SetCapabilities(); @@ -73,6 +75,6 @@ namespace NzbDrone.Core.Indexers.Definitions.PassThePopcorn public class PassThePopcornFlag : IndexerFlag { public static IndexerFlag Golden => new ("golden", "Release follows Golden Popcorn quality rules"); - public static IndexerFlag Approved => new ("approved", "Release approved by PTP"); + public static IndexerFlag Approved => new ("approved", "Release approved by PTP staff"); } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornParser.cs b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornParser.cs index 997ba97e0..7b6fe586e 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornParser.cs @@ -56,6 +56,19 @@ namespace NzbDrone.Core.Indexers.Definitions.PassThePopcorn { foreach (var torrent in result.Torrents) { + // skip non-freeleech results when freeleech only is set + var downloadVolumeFactor = torrent.FreeleechType?.ToUpperInvariant() switch + { + "FREELEECH" or "NEUTRAL LEECH" => 0, + "HALF LEECH" => 0.5, + _ => 1 + }; + + if (_settings.FreeleechOnly && downloadVolumeFactor != 0.0) + { + continue; + } + var id = torrent.Id; var title = torrent.ReleaseName; @@ -78,6 +91,12 @@ namespace NzbDrone.Core.Indexers.Definitions.PassThePopcorn categories.Add(NewznabStandardCategory.TV); } + var uploadVolumeFactor = torrent.FreeleechType?.ToUpperInvariant() switch + { + "NEUTRAL LEECH" => 0, + _ => 1 + }; + torrentInfos.Add(new TorrentInfo { Guid = $"PassThePopcorn-{id}", @@ -94,13 +113,8 @@ namespace NzbDrone.Core.Indexers.Definitions.PassThePopcorn ImdbId = result.ImdbId.IsNotNullOrWhiteSpace() ? int.Parse(result.ImdbId) : 0, Scene = torrent.Scene, IndexerFlags = flags, - DownloadVolumeFactor = torrent.FreeleechType?.ToUpperInvariant() switch - { - "FREELEECH" => 0, - "HALF LEECH" => 0.5, - _ => 1 - }, - UploadVolumeFactor = 1, + DownloadVolumeFactor = downloadVolumeFactor, + UploadVolumeFactor = uploadVolumeFactor, MinimumRatio = 1, MinimumSeedTime = 345600, Genres = result.Tags ?? new List<string>(), diff --git a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornRequestGenerator.cs index 17d9b1813..90b370e7b 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornRequestGenerator.cs @@ -86,6 +86,11 @@ namespace NzbDrone.Core.Indexers.Definitions.PassThePopcorn parameters.Set("freetorrent", "1"); } + if (_settings.GoldenPopcornOnly) + { + parameters.Set("scene", "2"); + } + var queryCats = _capabilities.Categories .MapTorznabCapsToTrackers(searchCriteria.Categories) .Select(int.Parse) diff --git a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornSettings.cs index 08ae2284c..5494ff989 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornSettings.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornSettings.cs @@ -27,6 +27,9 @@ namespace NzbDrone.Core.Indexers.Definitions.PassThePopcorn [FieldDefinition(4, Label = "IndexerSettingsFreeleechOnly", HelpText = "IndexerPassThePopcornSettingsFreeleechOnlyHelpText", Type = FieldType.Checkbox)] public bool FreeleechOnly { get; set; } + [FieldDefinition(5, Label = "IndexerPassThePopcornSettingsGoldenPopcornOnly", HelpText = "IndexerPassThePopcornSettingsGoldenPopcornOnlyHelpText", Type = FieldType.Checkbox, Advanced = true)] + public bool GoldenPopcornOnly { get; set; } + public override NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); diff --git a/src/NzbDrone.Core/Indexers/Definitions/PixelHD.cs b/src/NzbDrone.Core/Indexers/Definitions/PixelHD.cs index 1c78174ca..98549f370 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/PixelHD.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/PixelHD.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Globalization; +using System.Linq; using System.Net; using System.Text; using AngleSharp.Html.Parser; @@ -178,8 +179,7 @@ public class PixelHDParser : IParseIndexerResponse { var groupName = group.QuerySelector("strong:has(a[title=\"View Torrent\"])")?.TextContent.Replace(" ]", "]"); - var imdbLink = group.QuerySelector("a[href*=\"imdb.com/title/tt\"]")?.GetAttribute("href"); - var imdbId = ParseUtil.GetImdbId(imdbLink) ?? 0; + var imdbId = ParseUtil.GetImdbId(group.QuerySelector("a[href*=\"imdb.com/title/tt\"]")?.GetAttribute("href")?.TrimEnd('/')?.Split('/')?.LastOrDefault()) ?? 0; var rows = group.QuerySelectorAll("tr.group_torrent:has(a[href^=\"torrents.php?id=\"])"); foreach (var row in rows) diff --git a/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs b/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs index 2af30fb10..963bb5a2a 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs @@ -8,6 +8,7 @@ using FluentValidation; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; +using NzbDrone.Common.Serializer; using NzbDrone.Core.Annotations; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers.Definitions.Gazelle; @@ -24,7 +25,8 @@ namespace NzbDrone.Core.Indexers.Definitions public class Redacted : TorrentIndexerBase<RedactedSettings> { public override string Name => "Redacted"; - public override string[] IndexerUrls => new[] { "https://redacted.ch/" }; + public override string[] IndexerUrls => new[] { "https://redacted.sh/" }; + public override string[] LegacyUrls => new[] { "https://redacted.ch/" }; public override string Description => "REDActed (Aka.PassTheHeadPhones) is one of the most well-known music trackers."; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); @@ -102,6 +104,32 @@ namespace NzbDrone.Core.Indexers.Definitions return caps; } + + public override async Task<IndexerDownloadResponse> Download(Uri link) + { + var downloadResponse = await base.Download(link); + + var fileData = downloadResponse.Data; + + if (Settings.UseFreeleechToken == (int)RedactedFreeleechTokenAction.Preferred + && fileData.Length >= 1 + && fileData[0] != 'd' // simple test for torrent vs HTML content + && link.Query.Contains("usetoken=1")) + { + var html = Encoding.GetString(fileData); + + if (html.Contains("You do not have any freeleech tokens left.") + || html.Contains("You do not have enough freeleech tokens") + || html.Contains("This torrent is too large.") + || html.Contains("You cannot use tokens here")) + { + // Try to download again without usetoken + downloadResponse = await base.Download(link.RemoveQueryParam("usetoken")); + } + } + + return downloadResponse; + } } public class RedactedRequestGenerator : IIndexerRequestGenerator @@ -225,7 +253,9 @@ namespace NzbDrone.Core.Indexers.Definitions if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK) { - throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request"); + STJson.TryDeserialize<GazelleErrorResponse>(indexerResponse.Content, out var errorResponse); + + throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request: {errorResponse?.Error ?? "Check the logs for more information."}"); } if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value)) @@ -248,7 +278,7 @@ namespace NzbDrone.Core.Indexers.Definitions foreach (var torrent in result.Torrents) { // skip releases that cannot be used with freeleech tokens when the option is enabled - if (_settings.UseFreeleechToken && !torrent.CanUseToken) + if (_settings.UseFreeleechToken == (int)RedactedFreeleechTokenAction.Required && !torrent.CanUseToken) { continue; } @@ -263,12 +293,13 @@ namespace NzbDrone.Core.Indexers.Definitions var title = GetTitle(result, torrent); var infoUrl = GetInfoUrl(result.GroupId, id); + var isFreeLeech = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsFreeload || torrent.IsPersonalFreeLeech; var release = new TorrentInfo { Guid = infoUrl, InfoUrl = infoUrl, - DownloadUrl = GetDownloadUrl(id, !torrent.IsFreeLeech && !torrent.IsNeutralLeech && !torrent.IsFreeload && !torrent.IsPersonalFreeLeech), + DownloadUrl = GetDownloadUrl(id, torrent.CanUseToken && !isFreeLeech), Title = WebUtility.HtmlDecode(title), Artist = WebUtility.HtmlDecode(result.Artist), Album = WebUtility.HtmlDecode(result.GroupName), @@ -282,7 +313,7 @@ namespace NzbDrone.Core.Indexers.Definitions Scene = torrent.Scene, Files = torrent.FileCount, Grabs = torrent.Snatches, - DownloadVolumeFactor = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsFreeload || torrent.IsPersonalFreeLeech ? 0 : 1, + DownloadVolumeFactor = isFreeLeech ? 0 : 1, UploadVolumeFactor = torrent.IsNeutralLeech || torrent.IsFreeload ? 0 : 1 }; @@ -304,7 +335,7 @@ namespace NzbDrone.Core.Indexers.Definitions else { // skip releases that cannot be used with freeleech tokens when the option is enabled - if (_settings.UseFreeleechToken && !result.CanUseToken) + if (_settings.UseFreeleechToken == (int)RedactedFreeleechTokenAction.Required && !result.CanUseToken) { continue; } @@ -317,12 +348,13 @@ namespace NzbDrone.Core.Indexers.Definitions var id = result.TorrentId; var infoUrl = GetInfoUrl(result.GroupId, id); + var isFreeLeech = result.IsFreeLeech || result.IsNeutralLeech || result.IsFreeload || result.IsPersonalFreeLeech; var release = new TorrentInfo { Guid = infoUrl, InfoUrl = infoUrl, - DownloadUrl = GetDownloadUrl(id, !result.IsFreeLeech && !result.IsNeutralLeech && !result.IsFreeload && !result.IsPersonalFreeLeech), + DownloadUrl = GetDownloadUrl(id, result.CanUseToken && !isFreeLeech), Title = WebUtility.HtmlDecode(result.GroupName), Size = long.Parse(result.Size), Seeders = int.Parse(result.Seeders), @@ -330,7 +362,7 @@ namespace NzbDrone.Core.Indexers.Definitions PublishDate = DateTimeOffset.FromUnixTimeSeconds(ParseUtil.CoerceLong(result.GroupTime)).UtcDateTime, Files = result.FileCount, Grabs = result.Snatches, - DownloadVolumeFactor = result.IsFreeLeech || result.IsNeutralLeech || result.IsFreeload || result.IsPersonalFreeLeech ? 0 : 1, + DownloadVolumeFactor = isFreeLeech ? 0 : 1, UploadVolumeFactor = result.IsNeutralLeech || result.IsFreeload ? 0 : 1 }; @@ -395,7 +427,7 @@ namespace NzbDrone.Core.Indexers.Definitions .AddQueryParam("action", "download") .AddQueryParam("id", torrentId); - if (_settings.UseFreeleechToken && canUseToken) + if (_settings.UseFreeleechToken is (int)RedactedFreeleechTokenAction.Preferred or (int)RedactedFreeleechTokenAction.Required && canUseToken) { url = url.AddQueryParam("usetoken", "1"); } @@ -429,14 +461,14 @@ namespace NzbDrone.Core.Indexers.Definitions public RedactedSettings() { Apikey = ""; - UseFreeleechToken = false; + UseFreeleechToken = (int)RedactedFreeleechTokenAction.Never; } [FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "IndexerRedactedSettingsApiKeyHelpText")] public string Apikey { get; set; } - [FieldDefinition(3, Label = "Use Freeleech Tokens", Type = FieldType.Checkbox, HelpText = "Use freeleech tokens when available")] - public bool UseFreeleechToken { get; set; } + [FieldDefinition(3, Type = FieldType.Select, Label = "Use Freeleech Tokens", SelectOptions = typeof(RedactedFreeleechTokenAction), HelpText = "When to use freeleech tokens")] + public int UseFreeleechToken { get; set; } [FieldDefinition(4, Label = "Freeload Only", Type = FieldType.Checkbox, Advanced = true, HelpTextWarning = "Search freeload torrents only. End date: 31 January 2024, 23:59 UTC.")] public bool FreeloadOnly { get; set; } @@ -446,4 +478,16 @@ namespace NzbDrone.Core.Indexers.Definitions return new NzbDroneValidationResult(Validator.Validate(this)); } } + + public enum RedactedFreeleechTokenAction + { + [FieldOption(Label = "Never", Hint = "Do not use tokens")] + Never = 0, + + [FieldOption(Label = "Preferred", Hint = "Use token if possible")] + Preferred = 1, + + [FieldOption(Label = "Required", Hint = "Abort download if unable to use token")] + Required = 2, + } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/RuTracker.cs b/src/NzbDrone.Core/Indexers/Definitions/RuTracker.cs index 78f92df52..44f6bae38 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/RuTracker.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/RuTracker.cs @@ -30,7 +30,7 @@ namespace NzbDrone.Core.Indexers.Definitions "https://rutracker.net/", "https://rutracker.nl/" }; - public override string Description => "RuTracker.org is a Semi-Private Russian torrent site with a thriving file-sharing community"; + public override string Description => "RuTracker.org is a RUSSIAN Semi-Private site with a thriving file-sharing community"; public override string Language => "ru-RU"; public override Encoding Encoding => Encoding.GetEncoding("windows-1251"); public override IndexerPrivacy Privacy => IndexerPrivacy.SemiPrivate; @@ -51,7 +51,7 @@ namespace NzbDrone.Core.Indexers.Definitions return new RuTrackerParser(Settings, Capabilities.Categories); } - public override async Task<byte[]> Download(Uri link) + public override async Task<IndexerDownloadResponse> Download(Uri link) { if (Settings.UseMagnetLinks && link.PathAndQuery.Contains("viewtopic.php?t=")) { @@ -144,6 +144,7 @@ namespace NzbDrone.Core.Indexers.Definitions SupportsRawSearch = true }; + // Note: When refreshing the categories use the tracker.php page and NOT the search.php page! caps.Categories.AddCategoryMapping(22, NewznabStandardCategory.Movies, "Наше кино"); caps.Categories.AddCategoryMapping(941, NewznabStandardCategory.Movies, "|- Кино СССР"); caps.Categories.AddCategoryMapping(1666, NewznabStandardCategory.Movies, "|- Детские отечественные фильмы"); @@ -157,8 +158,8 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(2092, NewznabStandardCategory.MoviesForeign, "|- Фильмы 2006-2010"); caps.Categories.AddCategoryMapping(2093, NewznabStandardCategory.MoviesForeign, "|- Фильмы 2011-2015"); caps.Categories.AddCategoryMapping(2200, NewznabStandardCategory.MoviesForeign, "|- Фильмы 2016-2020"); - caps.Categories.AddCategoryMapping(1950, NewznabStandardCategory.MoviesForeign, "|- Фильмы 2021-2022"); - caps.Categories.AddCategoryMapping(252, NewznabStandardCategory.MoviesForeign, "|- Фильмы 2023"); + caps.Categories.AddCategoryMapping(1950, NewznabStandardCategory.MoviesForeign, "|- Фильмы 2021-2023"); + caps.Categories.AddCategoryMapping(252, NewznabStandardCategory.MoviesForeign, "|- Фильмы 2024"); caps.Categories.AddCategoryMapping(2540, NewznabStandardCategory.MoviesForeign, "|- Фильмы Ближнего Зарубежья"); caps.Categories.AddCategoryMapping(934, NewznabStandardCategory.MoviesForeign, "|- Азиатские фильмы"); caps.Categories.AddCategoryMapping(505, NewznabStandardCategory.MoviesForeign, "|- Индийское кино"); @@ -172,6 +173,7 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(709, NewznabStandardCategory.MoviesOther, "|- Документальные фильмы (Арт-хаус и авторское кино)"); caps.Categories.AddCategoryMapping(1577, NewznabStandardCategory.MoviesOther, "|- Анимация (Арт-хаус и авторское кино)"); caps.Categories.AddCategoryMapping(511, NewznabStandardCategory.TVOther, "Театр"); + caps.Categories.AddCategoryMapping(1493, NewznabStandardCategory.TVOther, "|- Спектакли без перевода"); caps.Categories.AddCategoryMapping(93, NewznabStandardCategory.MoviesDVD, "DVD Video"); caps.Categories.AddCategoryMapping(905, NewznabStandardCategory.MoviesDVD, "|- Классика мирового кинематографа (DVD Video)"); caps.Categories.AddCategoryMapping(101, NewznabStandardCategory.MoviesDVD, "|- Зарубежное кино (DVD Video)"); @@ -212,6 +214,7 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(521, NewznabStandardCategory.MoviesDVD, "|- Иностранные мультфильмы (DVD)"); caps.Categories.AddCategoryMapping(208, NewznabStandardCategory.Movies, "|- Отечественные мультфильмы"); caps.Categories.AddCategoryMapping(539, NewznabStandardCategory.Movies, "|- Отечественные полнометражные мультфильмы"); + caps.Categories.AddCategoryMapping(2183, NewznabStandardCategory.MoviesForeign, "|- Мультфильмы Ближнего Зарубежья"); caps.Categories.AddCategoryMapping(209, NewznabStandardCategory.MoviesForeign, "|- Иностранные мультфильмы"); caps.Categories.AddCategoryMapping(484, NewznabStandardCategory.MoviesForeign, "|- Иностранные короткометражные мультфильмы"); caps.Categories.AddCategoryMapping(822, NewznabStandardCategory.Movies, "|- Сборники мультфильмов"); @@ -391,19 +394,21 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(114, NewznabStandardCategory.TVOther, "|- [Видео Юмор] Сатирики и юмористы"); caps.Categories.AddCategoryMapping(1332, NewznabStandardCategory.TVOther, "|- Юмористические аудиопередачи"); caps.Categories.AddCategoryMapping(1495, NewznabStandardCategory.TVOther, "|- Аудио и видео ролики (Приколы и юмор)"); - caps.Categories.AddCategoryMapping(1392, NewznabStandardCategory.TVSport, "XXXII Летние Олимпийские игры 2020"); - caps.Categories.AddCategoryMapping(2475, NewznabStandardCategory.TVSport, "|- Легкая атлетика"); - caps.Categories.AddCategoryMapping(2493, NewznabStandardCategory.TVSport, "|- Плавание. Прыжки в воду. Синхронное плавание"); - caps.Categories.AddCategoryMapping(2113, NewznabStandardCategory.TVSport, "|- Спортивная гимнастика. Художественная гимнастика. Прыжки на батуте"); - caps.Categories.AddCategoryMapping(2482, NewznabStandardCategory.TVSport, "|- Велоспорт"); - caps.Categories.AddCategoryMapping(2103, NewznabStandardCategory.TVSport, "|- Академическая гребля. Гребля на байдарках и каноэ"); - caps.Categories.AddCategoryMapping(2522, NewznabStandardCategory.TVSport, "|- Бокс. Борьба Вольная и Греко-римская. Дзюдо. Карате. Тхэквондо"); - caps.Categories.AddCategoryMapping(2485, NewznabStandardCategory.TVSport, "|- Футбол"); - caps.Categories.AddCategoryMapping(2486, NewznabStandardCategory.TVSport, "|- Баскетбол. Волейбол. Гандбол. Водное поло. Регби. Хоккей на траве"); + caps.Categories.AddCategoryMapping(1346, NewznabStandardCategory.TVSport, "XXXIII Летние Олимпийские игры 2024"); + caps.Categories.AddCategoryMapping(2493, NewznabStandardCategory.TVSport, "|- Легкая атлетика. Плавание. Прыжки в воду. Синхронное плавание. Гим.."); + caps.Categories.AddCategoryMapping(2103, NewznabStandardCategory.TVSport, "|- Велоспорт. Академическая гребля. Гребля на байдарках и каноэ"); + caps.Categories.AddCategoryMapping(2485, NewznabStandardCategory.TVSport, "|- Футбол. Баскетбол. Волейбол. Гандбол. Водное поло. Регби. Хоккей н.."); caps.Categories.AddCategoryMapping(2479, NewznabStandardCategory.TVSport, "|- Теннис. Настольный теннис. Бадминтон"); caps.Categories.AddCategoryMapping(2089, NewznabStandardCategory.TVSport, "|- Фехтование. Стрельба. Стрельба из лука. Современное пятиборье"); + caps.Categories.AddCategoryMapping(2338, NewznabStandardCategory.TVSport, "|- Бокс. Борьба Вольная и Греко-римская. Дзюдо. Карате. Тхэквондо"); + caps.Categories.AddCategoryMapping(927, NewznabStandardCategory.TVSport, "|- Другие виды спорта"); + caps.Categories.AddCategoryMapping(1392, NewznabStandardCategory.TVSport, "XXXII Летние Олимпийские игры 2020"); + caps.Categories.AddCategoryMapping(2475, NewznabStandardCategory.TVSport, "|- Легкая атлетика. Плавание. Прыжки в воду. Синхронное плавание"); + caps.Categories.AddCategoryMapping(2113, NewznabStandardCategory.TVSport, "|- Гимнастика. Прыжки на батуте. Фехтование. Стрельба. Современное пя.."); + caps.Categories.AddCategoryMapping(2482, NewznabStandardCategory.TVSport, "|- Велоспорт. Академическая гребля. Гребля на байдарках и каноэ"); + caps.Categories.AddCategoryMapping(2522, NewznabStandardCategory.TVSport, "|- Бокс. Борьба Вольная и Греко-римская. Дзюдо. Карате. Тхэквондо"); + caps.Categories.AddCategoryMapping(2486, NewznabStandardCategory.TVSport, "|- Баскетбол. Волейбол. Гандбол. Водное поло. Регби. Хоккей на траве"); caps.Categories.AddCategoryMapping(1794, NewznabStandardCategory.TVSport, "|- Другие виды спорта"); - caps.Categories.AddCategoryMapping(2338, NewznabStandardCategory.TVSport, "|- Обзорные и аналитические программы"); caps.Categories.AddCategoryMapping(1315, NewznabStandardCategory.TVSport, "XXIV Зимние Олимпийские игры 2022"); caps.Categories.AddCategoryMapping(1336, NewznabStandardCategory.TVSport, "|- Биатлон"); caps.Categories.AddCategoryMapping(2171, NewznabStandardCategory.TVSport, "|- Лыжные гонки"); @@ -418,8 +423,8 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(255, NewznabStandardCategory.TVSport, "Спортивные турниры, фильмы и передачи"); caps.Categories.AddCategoryMapping(256, NewznabStandardCategory.TVSport, "|- Автоспорт"); caps.Categories.AddCategoryMapping(1986, NewznabStandardCategory.TVSport, "|- Мотоспорт"); - caps.Categories.AddCategoryMapping(660, NewznabStandardCategory.TVSport, "|- Формула-1 (2022)"); - caps.Categories.AddCategoryMapping(1551, NewznabStandardCategory.TVSport, "|- Формула-1 (2012-2021)"); + caps.Categories.AddCategoryMapping(660, NewznabStandardCategory.TVSport, "|- Формула-1 (2024)"); + caps.Categories.AddCategoryMapping(1551, NewznabStandardCategory.TVSport, "|- Формула-1 (2012-2023)"); caps.Categories.AddCategoryMapping(626, NewznabStandardCategory.TVSport, "|- Формула 1 (до 2011 вкл.)"); caps.Categories.AddCategoryMapping(262, NewznabStandardCategory.TVSport, "|- Велоспорт"); caps.Categories.AddCategoryMapping(1326, NewznabStandardCategory.TVSport, "|- Волейбол/Гандбол"); @@ -443,16 +448,16 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(1319, NewznabStandardCategory.TVSport, "|- Спорт (видео)"); caps.Categories.AddCategoryMapping(1608, NewznabStandardCategory.TVSport, "⚽ Футбол"); caps.Categories.AddCategoryMapping(2294, NewznabStandardCategory.TVSport, "|- UHDTV"); - caps.Categories.AddCategoryMapping(1229, NewznabStandardCategory.TVSport, "|- Чемпионат Мира 2022 (финальный турнир)"); - caps.Categories.AddCategoryMapping(1693, NewznabStandardCategory.TVSport, "|- Чемпионат Мира 2022 (отбор)"); - caps.Categories.AddCategoryMapping(2532, NewznabStandardCategory.TVSport, "|- Чемпионат Европы 2020 [2021] (финальный турнир)"); + caps.Categories.AddCategoryMapping(1693, NewznabStandardCategory.TVSport, "|- Чемпионат Мира 2026 (отбор)"); caps.Categories.AddCategoryMapping(136, NewznabStandardCategory.TVSport, "|- Чемпионат Европы 2024 (отбор)"); + caps.Categories.AddCategoryMapping(2532, NewznabStandardCategory.TVSport, "|- Чемпионат Европы 2020 [2021] (финальный турнир)"); caps.Categories.AddCategoryMapping(592, NewznabStandardCategory.TVSport, "|- Лига Наций"); + caps.Categories.AddCategoryMapping(1229, NewznabStandardCategory.TVSport, "|- Чемпионат Мира 2022"); caps.Categories.AddCategoryMapping(2533, NewznabStandardCategory.TVSport, "|- Чемпионат Мира 2018 (игры)"); caps.Categories.AddCategoryMapping(1952, NewznabStandardCategory.TVSport, "|- Чемпионат Мира 2018 (обзорные передачи, документалистика)"); caps.Categories.AddCategoryMapping(1621, NewznabStandardCategory.TVSport, "|- Чемпионаты Мира"); - caps.Categories.AddCategoryMapping(2075, NewznabStandardCategory.TVSport, "|- Россия 2022-2023"); - caps.Categories.AddCategoryMapping(1668, NewznabStandardCategory.TVSport, "|- Россия 2021-2022"); + caps.Categories.AddCategoryMapping(2075, NewznabStandardCategory.TVSport, "|- Россия 2024-2025"); + caps.Categories.AddCategoryMapping(1668, NewznabStandardCategory.TVSport, "|- Россия 2023-2024"); caps.Categories.AddCategoryMapping(1613, NewznabStandardCategory.TVSport, "|- Россия/СССР"); caps.Categories.AddCategoryMapping(1614, NewznabStandardCategory.TVSport, "|- Англия"); caps.Categories.AddCategoryMapping(1623, NewznabStandardCategory.TVSport, "|- Испания"); @@ -462,13 +467,13 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(2514, NewznabStandardCategory.TVSport, "|- Украина"); caps.Categories.AddCategoryMapping(1616, NewznabStandardCategory.TVSport, "|- Другие национальные чемпионаты и кубки"); caps.Categories.AddCategoryMapping(2014, NewznabStandardCategory.TVSport, "|- Международные турниры"); - caps.Categories.AddCategoryMapping(1442, NewznabStandardCategory.TVSport, "|- Еврокубки 2022-2023"); - caps.Categories.AddCategoryMapping(1491, NewznabStandardCategory.TVSport, "|- Еврокубки 2021-2022"); - caps.Categories.AddCategoryMapping(1987, NewznabStandardCategory.TVSport, "|- Еврокубки 2011-2021"); + caps.Categories.AddCategoryMapping(1442, NewznabStandardCategory.TVSport, "|- Еврокубки 2024-2025"); + caps.Categories.AddCategoryMapping(1491, NewznabStandardCategory.TVSport, "|- Еврокубки 2023-2024"); + caps.Categories.AddCategoryMapping(1987, NewznabStandardCategory.TVSport, "|- Еврокубки 2011-2023"); caps.Categories.AddCategoryMapping(1617, NewznabStandardCategory.TVSport, "|- Еврокубки"); caps.Categories.AddCategoryMapping(1620, NewznabStandardCategory.TVSport, "|- Чемпионаты Европы"); caps.Categories.AddCategoryMapping(1998, NewznabStandardCategory.TVSport, "|- Товарищеские турниры и матчи"); - caps.Categories.AddCategoryMapping(1343, NewznabStandardCategory.TVSport, "|- Обзорные и аналитические передачи 2018-2022"); + caps.Categories.AddCategoryMapping(1343, NewznabStandardCategory.TVSport, "|- Обзорные и аналитические передачи 2018-2023"); caps.Categories.AddCategoryMapping(751, NewznabStandardCategory.TVSport, "|- Обзорные и аналитические передачи"); caps.Categories.AddCategoryMapping(497, NewznabStandardCategory.TVSport, "|- Документальные фильмы (футбол)"); caps.Categories.AddCategoryMapping(1697, NewznabStandardCategory.TVSport, "|- Мини-футбол/Пляжный футбол"); @@ -476,7 +481,7 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(2001, NewznabStandardCategory.TVSport, "|- Международные соревнования"); caps.Categories.AddCategoryMapping(2002, NewznabStandardCategory.TVSport, "|- NBA / NCAA (до 2000 г.)"); caps.Categories.AddCategoryMapping(283, NewznabStandardCategory.TVSport, "|- NBA / NCAA (2000-2010 гг.)"); - caps.Categories.AddCategoryMapping(1997, NewznabStandardCategory.TVSport, "|- NBA / NCAA (2010-2023 гг.)"); + caps.Categories.AddCategoryMapping(1997, NewznabStandardCategory.TVSport, "|- NBA / NCAA (2010-2024 гг.)"); caps.Categories.AddCategoryMapping(2003, NewznabStandardCategory.TVSport, "|- Европейский клубный баскетбол"); caps.Categories.AddCategoryMapping(2009, NewznabStandardCategory.TVSport, "🏒 Хоккей"); caps.Categories.AddCategoryMapping(2010, NewznabStandardCategory.TVSport, "|- Хоккей с мячом / Бенди"); @@ -678,9 +683,9 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(2441, NewznabStandardCategory.BooksEBook, "|- Кулинария. Цветоводство. Домоводство"); caps.Categories.AddCategoryMapping(2442, NewznabStandardCategory.BooksEBook, "|- Культура. Искусство. История"); caps.Categories.AddCategoryMapping(2125, NewznabStandardCategory.Books, "Медицина и здоровье"); - caps.Categories.AddCategoryMapping(2133, NewznabStandardCategory.Books, "|- Клиническая медицина до 1980 г."); - caps.Categories.AddCategoryMapping(2130, NewznabStandardCategory.Books, "|- Клиническая медицина с 1980 по 2000 г."); - caps.Categories.AddCategoryMapping(2313, NewznabStandardCategory.Books, "|- Клиническая медицина после 2000 г."); + caps.Categories.AddCategoryMapping(2133, NewznabStandardCategory.Books, "|- Клиническая медицина до 1980 год"); + caps.Categories.AddCategoryMapping(2130, NewznabStandardCategory.Books, "|- Клиническая медицина с 1980 по 2000 год"); + caps.Categories.AddCategoryMapping(2313, NewznabStandardCategory.Books, "|- Клиническая медицина после 2000 год"); caps.Categories.AddCategoryMapping(2528, NewznabStandardCategory.Books, "|- Научная медицинская периодика (газеты и журналы)"); caps.Categories.AddCategoryMapping(2129, NewznabStandardCategory.Books, "|- Медико-биологические науки"); caps.Categories.AddCategoryMapping(2141, NewznabStandardCategory.Books, "|- Фармация и фармакология"); @@ -771,6 +776,7 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(1592, NewznabStandardCategory.BooksOther, "|- Ушу"); caps.Categories.AddCategoryMapping(1595, NewznabStandardCategory.BooksOther, "|- Разное"); caps.Categories.AddCategoryMapping(1556, NewznabStandardCategory.BooksTechnical, "Компьютерные видеоуроки и обучающие интерактивные DVD"); + caps.Categories.AddCategoryMapping(2539, NewznabStandardCategory.BooksTechnical, "|- Machine/Deep Learning, Neural Networks"); caps.Categories.AddCategoryMapping(1560, NewznabStandardCategory.BooksTechnical, "|- Компьютерные сети и безопасность"); caps.Categories.AddCategoryMapping(1991, NewznabStandardCategory.BooksTechnical, "|- Devops"); caps.Categories.AddCategoryMapping(1561, NewznabStandardCategory.BooksTechnical, "|- ОС и серверные программы Microsoft"); @@ -928,9 +934,13 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(1224, NewznabStandardCategory.AudioLossless, "|- Авторская песня (lossless)"); caps.Categories.AddCategoryMapping(1225, NewznabStandardCategory.AudioMP3, "|- Авторская песня (lossy)"); caps.Categories.AddCategoryMapping(1226, NewznabStandardCategory.Audio, "|- Менестрели и ролевики (lossy и lossless)"); + caps.Categories.AddCategoryMapping(782, NewznabStandardCategory.Audio, "Лейбл- и сцен-паки. Неофициальные сборники и ремастеринги. AI-музыка"); + caps.Categories.AddCategoryMapping(577, NewznabStandardCategory.Audio, "|- AI-Music - музыка ИИ, нейросетей (lossy и lossless)"); caps.Categories.AddCategoryMapping(1842, NewznabStandardCategory.AudioLossless, "Label Packs (lossless)"); caps.Categories.AddCategoryMapping(1648, NewznabStandardCategory.AudioMP3, "Label packs, Scene packs (lossy)"); - caps.Categories.AddCategoryMapping(2495, NewznabStandardCategory.Audio, "Отечественная поп-музыка"); + caps.Categories.AddCategoryMapping(134, NewznabStandardCategory.AudioLossless, "|- Неофициальные сборники и ремастеринги (lossless)"); + caps.Categories.AddCategoryMapping(965, NewznabStandardCategory.AudioMP3, "|- Неофициальные сборники (lossy)"); + caps.Categories.AddCategoryMapping(2495, NewznabStandardCategory.AudioMP3, "Отечественная поп-музыка "); caps.Categories.AddCategoryMapping(424, NewznabStandardCategory.AudioMP3, "|- Популярная музыка России и стран бывшего СССР (lossy)"); caps.Categories.AddCategoryMapping(1361, NewznabStandardCategory.AudioMP3, "|- Популярная музыка России и стран бывшего СССР (сборники) (lossy)"); caps.Categories.AddCategoryMapping(425, NewznabStandardCategory.AudioLossless, "|- Популярная музыка России и стран бывшего СССР (lossless)"); @@ -1172,49 +1182,50 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(1912, NewznabStandardCategory.AudioVideo, "|- Электронная музыка (Видео)"); caps.Categories.AddCategoryMapping(1189, NewznabStandardCategory.AudioVideo, "|- Документальные фильмы о музыке и музыкантах (Видео)"); caps.Categories.AddCategoryMapping(2403, NewznabStandardCategory.AudioVideo, "Музыкальное DVD видео"); - caps.Categories.AddCategoryMapping(984, NewznabStandardCategory.AudioVideo, "|- Классическая и современная академическая музыка (DVD Video)"); - caps.Categories.AddCategoryMapping(983, NewznabStandardCategory.AudioVideo, "|- Опера, Оперетта и Мюзикл (DVD видео)"); - caps.Categories.AddCategoryMapping(2352, NewznabStandardCategory.AudioVideo, "|- Балет и современная хореография (DVD Video)"); - caps.Categories.AddCategoryMapping(2384, NewznabStandardCategory.AudioVideo, "|- Классика в современной обработке, Classical Crossover (DVD Video)"); - caps.Categories.AddCategoryMapping(1142, NewznabStandardCategory.AudioVideo, "|- Фольклор, Народная и Этническая музыка и Flamenco (DVD Video)"); - caps.Categories.AddCategoryMapping(1107, NewznabStandardCategory.AudioVideo, "|- New Age, Relax, Meditative, Рэп, Хип-Хоп, R'n'B, Reggae, Ska, Dub (DVD Video)"); - caps.Categories.AddCategoryMapping(1228, NewznabStandardCategory.AudioVideo, "|- Зарубежный и Отечественный Шансон, Авторская и Военная песня (DVD Video)"); - caps.Categories.AddCategoryMapping(988, NewznabStandardCategory.AudioVideo, "|- Музыка других жанров, Советская эстрада, ретро, романсы (DVD Video)"); - caps.Categories.AddCategoryMapping(1122, NewznabStandardCategory.AudioVideo, "|- Отечественная поп-музыка (DVD Video)"); - caps.Categories.AddCategoryMapping(986, NewznabStandardCategory.AudioVideo, "|- Зарубежная Поп-музыка, Eurodance, Disco (DVD Video)"); - caps.Categories.AddCategoryMapping(2379, NewznabStandardCategory.AudioVideo, "|- Восточноазиатская поп-музыка (DVD Video)"); - caps.Categories.AddCategoryMapping(2088, NewznabStandardCategory.AudioVideo, "|- Разножанровые сборные концерты и сборники видеоклипов (DVD Video)"); + caps.Categories.AddCategoryMapping(984, NewznabStandardCategory.AudioVideo, "|- Классическая и современная академическая музыка (DVD Видео)"); + caps.Categories.AddCategoryMapping(983, NewznabStandardCategory.AudioVideo, "|- Опера, Оперетта и Мюзикл (DVD Видео)"); + caps.Categories.AddCategoryMapping(2352, NewznabStandardCategory.AudioVideo, "|- Балет и современная хореография (DVD Видео)"); + caps.Categories.AddCategoryMapping(2384, NewznabStandardCategory.AudioVideo, "|- Классика в современной обработке, Classical Crossover (DVD Видео)"); + caps.Categories.AddCategoryMapping(1142, NewznabStandardCategory.AudioVideo, "|- Фольклор, Народная и Этническая музыка и Flamenco (DVD Видео)"); + caps.Categories.AddCategoryMapping(1107, NewznabStandardCategory.AudioVideo, "|- New Age, Relax, Meditative, Рэп, Хип-Хоп, R'n'B, Reggae, Ska, Dub (DVD Видео)"); + caps.Categories.AddCategoryMapping(1228, NewznabStandardCategory.AudioVideo, "|- Зарубежный и Отечественный Шансон, Авторская и Военная песня (DVD Видео)"); + caps.Categories.AddCategoryMapping(988, NewznabStandardCategory.AudioVideo, "|- Музыка других жанров, Советская эстрада, ретро, романсы (DVD Видео)"); + caps.Categories.AddCategoryMapping(1122, NewznabStandardCategory.AudioVideo, "|- Отечественная поп-музыка (DVD Видео)"); + caps.Categories.AddCategoryMapping(986, NewznabStandardCategory.AudioVideo, "|- Зарубежная Поп-музыка, Eurodance, Disco (DVD Видео)"); + caps.Categories.AddCategoryMapping(2379, NewznabStandardCategory.AudioVideo, "|- Восточноазиатская поп-музыка (DVD Видео)"); + caps.Categories.AddCategoryMapping(2088, NewznabStandardCategory.AudioVideo, "|- Разножанровые сборные концерты и сборники видеоклипов (DVD Видео)"); caps.Categories.AddCategoryMapping(2304, NewznabStandardCategory.AudioVideo, "|- Джаз и Блюз (DVD Видео)"); - caps.Categories.AddCategoryMapping(1783, NewznabStandardCategory.AudioVideo, "|- Зарубежный Rock (DVD Video)"); - caps.Categories.AddCategoryMapping(1788, NewznabStandardCategory.AudioVideo, "|- Зарубежный Metal (DVD Video)"); - caps.Categories.AddCategoryMapping(1790, NewznabStandardCategory.AudioVideo, "|- Зарубежный Alternative, Punk, Independent (DVD Video)"); - caps.Categories.AddCategoryMapping(1792, NewznabStandardCategory.AudioVideo, "|- Отечественный Рок, Метал, Панк, Альтернатива (DVD Video)"); - caps.Categories.AddCategoryMapping(1886, NewznabStandardCategory.AudioVideo, "|- Электронная музыка (DVD Video)"); - caps.Categories.AddCategoryMapping(2509, NewznabStandardCategory.AudioVideo, "|- Документальные фильмы о музыке и музыкантах (DVD Video)"); + caps.Categories.AddCategoryMapping(1783, NewznabStandardCategory.AudioVideo, "|- Зарубежный Rock (DVD Видео)"); + caps.Categories.AddCategoryMapping(1788, NewznabStandardCategory.AudioVideo, "|- Зарубежный Metal (DVD Видео)"); + caps.Categories.AddCategoryMapping(1790, NewznabStandardCategory.AudioVideo, "|- Зарубежный Alternative, Punk, Independent (DVD Видео)"); + caps.Categories.AddCategoryMapping(1792, NewznabStandardCategory.AudioVideo, "|- Отечественный Рок, Метал, Панк, Альтернатива (DVD Видео)"); + caps.Categories.AddCategoryMapping(1886, NewznabStandardCategory.AudioVideo, "|- Электронная музыка (DVD Видео)"); + caps.Categories.AddCategoryMapping(2509, NewznabStandardCategory.AudioVideo, "|- Документальные фильмы о музыке и музыкантах (DVD Видео)"); caps.Categories.AddCategoryMapping(2507, NewznabStandardCategory.AudioVideo, "Неофициальные DVD видео"); - caps.Categories.AddCategoryMapping(2263, NewznabStandardCategory.AudioVideo, "|- Классическая музыка, Опера, Балет, Мюзикл (Неофициальные DVD Video)"); - caps.Categories.AddCategoryMapping(2511, NewznabStandardCategory.AudioVideo, "|- Шансон, Авторская песня, Сборные концерты, МДЖ (Неофициальные DVD Video)"); - caps.Categories.AddCategoryMapping(2264, NewznabStandardCategory.AudioVideo, "|- Зарубежная и Отечественная Поп-музыка (Неофициальные DVD Video)"); - caps.Categories.AddCategoryMapping(2262, NewznabStandardCategory.AudioVideo, "|- Джаз и Блюз (Неофициальные DVD Video)"); - caps.Categories.AddCategoryMapping(2261, NewznabStandardCategory.AudioVideo, "|- Зарубежная и Отечественная Рок-музыка (Неофициальные DVD Video)"); - caps.Categories.AddCategoryMapping(1887, NewznabStandardCategory.AudioVideo, "|- Электронная музыка (Неофициальные DVD Video)"); - caps.Categories.AddCategoryMapping(2531, NewznabStandardCategory.AudioVideo, "|- Прочие жанры (Неофициальные DVD видео)"); + caps.Categories.AddCategoryMapping(2263, NewznabStandardCategory.AudioVideo, "|- Классическая музыка, Опера, Балет, Мюзикл (Неофициальные DVD Видео)"); + caps.Categories.AddCategoryMapping(2511, NewznabStandardCategory.AudioVideo, "|- Шансон, Авторская песня, Сборные концерты, МДЖ (Неофициальные DVD Видео)"); + caps.Categories.AddCategoryMapping(2264, NewznabStandardCategory.AudioVideo, "|- Зарубежная и Отечественная Поп-музыка (Неофициальные DVD Видео)"); + caps.Categories.AddCategoryMapping(2262, NewznabStandardCategory.AudioVideo, "|- Джаз и Блюз (Неофициальные DVD Видео)"); + caps.Categories.AddCategoryMapping(2261, NewznabStandardCategory.AudioVideo, "|- Зарубежная и Отечественная Рок-музыка (Неофициальные DVD Видео)"); + caps.Categories.AddCategoryMapping(1887, NewznabStandardCategory.AudioVideo, "|- Электронная музыка (Неофициальные DVD Видео)"); + caps.Categories.AddCategoryMapping(2531, NewznabStandardCategory.AudioVideo, "|- Прочие жанры (Неофициальные DVD Видео)"); caps.Categories.AddCategoryMapping(2400, NewznabStandardCategory.AudioVideo, "Музыкальное HD видео"); - caps.Categories.AddCategoryMapping(1812, NewznabStandardCategory.AudioVideo, "|- Классическая и современная академическая музыка (HD Video)"); + caps.Categories.AddCategoryMapping(1812, NewznabStandardCategory.AudioVideo, "|- Классическая и современная академическая музыка (HD Видео)"); caps.Categories.AddCategoryMapping(655, NewznabStandardCategory.AudioVideo, "|- Опера, Оперетта и Мюзикл (HD Видео)"); - caps.Categories.AddCategoryMapping(1777, NewznabStandardCategory.AudioVideo, "|- Балет и современная хореография (HD Video)"); + caps.Categories.AddCategoryMapping(1777, NewznabStandardCategory.AudioVideo, "|- Балет и современная хореография (HD Видео)"); caps.Categories.AddCategoryMapping(2530, NewznabStandardCategory.AudioVideo, "|- Фольклор, Народная, Этническая музыка и Flamenco (HD Видео)"); caps.Categories.AddCategoryMapping(2529, NewznabStandardCategory.AudioVideo, "|- New Age, Relax, Meditative, Рэп, Хип-Хоп, R'n'B, Reggae, Ska, Dub (HD Видео)"); caps.Categories.AddCategoryMapping(1781, NewznabStandardCategory.AudioVideo, "|- Музыка других жанров, Разножанровые сборные концерты (HD видео)"); - caps.Categories.AddCategoryMapping(2508, NewznabStandardCategory.AudioVideo, "|- Зарубежная поп-музыка (HD Video)"); + caps.Categories.AddCategoryMapping(2508, NewznabStandardCategory.AudioVideo, "|- Зарубежная поп-музыка (HD Видео)"); caps.Categories.AddCategoryMapping(2426, NewznabStandardCategory.AudioVideo, "|- Отечественная поп-музыка (HD видео)"); - caps.Categories.AddCategoryMapping(2351, NewznabStandardCategory.AudioVideo, "|- Восточноазиатская Поп-музыка (HD Video)"); - caps.Categories.AddCategoryMapping(2306, NewznabStandardCategory.AudioVideo, "|- Джаз и Блюз (HD Video)"); - caps.Categories.AddCategoryMapping(1795, NewznabStandardCategory.AudioVideo, "|- Зарубежный рок (HD Video)"); + caps.Categories.AddCategoryMapping(2351, NewznabStandardCategory.AudioVideo, "|- Восточноазиатская Поп-музыка (HD Видео)"); + caps.Categories.AddCategoryMapping(2306, NewznabStandardCategory.AudioVideo, "|- Джаз и Блюз (HD Видео)"); + caps.Categories.AddCategoryMapping(1795, NewznabStandardCategory.AudioVideo, "|- Зарубежный рок (HD Видео)"); caps.Categories.AddCategoryMapping(2271, NewznabStandardCategory.AudioVideo, "|- Отечественный рок (HD видео)"); - caps.Categories.AddCategoryMapping(1913, NewznabStandardCategory.AudioVideo, "|- Электронная музыка (HD Video)"); + caps.Categories.AddCategoryMapping(1913, NewznabStandardCategory.AudioVideo, "|- Электронная музыка (HD Видео)"); caps.Categories.AddCategoryMapping(1784, NewznabStandardCategory.AudioVideo, "|- UHD музыкальное видео"); - caps.Categories.AddCategoryMapping(1892, NewznabStandardCategory.AudioVideo, "|- Документальные фильмы о музыке и музыкантах (HD Video)"); + caps.Categories.AddCategoryMapping(1892, NewznabStandardCategory.AudioVideo, "|- Документальные фильмы о музыке и музыкантах (HD Видео)"); + caps.Categories.AddCategoryMapping(2266, NewznabStandardCategory.AudioVideo, "|- Официальные апскейлы (Blu-ray, HDTV, WEB-DL)"); caps.Categories.AddCategoryMapping(518, NewznabStandardCategory.AudioVideo, "Некондиционное музыкальное видео (Видео, DVD видео, HD видео)"); caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.PCGames, "Игры для Windows"); caps.Categories.AddCategoryMapping(635, NewznabStandardCategory.PCGames, "|- Горячие Новинки"); @@ -1229,11 +1240,16 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(128, NewznabStandardCategory.PCGames, "|- Для самых маленьких"); caps.Categories.AddCategoryMapping(2204, NewznabStandardCategory.PCGames, "|- Логические игры"); caps.Categories.AddCategoryMapping(278, NewznabStandardCategory.PCGames, "|- Шахматы"); - caps.Categories.AddCategoryMapping(2118, NewznabStandardCategory.PCGames, "|- Многопользовательские игры"); caps.Categories.AddCategoryMapping(52, NewznabStandardCategory.PCGames, "|- Ролевые игры"); caps.Categories.AddCategoryMapping(54, NewznabStandardCategory.PCGames, "|- Симуляторы"); caps.Categories.AddCategoryMapping(51, NewznabStandardCategory.PCGames, "|- Стратегии в реальном времени"); caps.Categories.AddCategoryMapping(2226, NewznabStandardCategory.PCGames, "|- Пошаговые стратегии"); + caps.Categories.AddCategoryMapping(2118, NewznabStandardCategory.PCGames, "|- Антологии и сборники игр"); + caps.Categories.AddCategoryMapping(1310, NewznabStandardCategory.PCGames, "|- Старые игры (Экшены)"); + caps.Categories.AddCategoryMapping(2410, NewznabStandardCategory.PCGames, "|- Старые игры (Ролевые игры)"); + caps.Categories.AddCategoryMapping(2205, NewznabStandardCategory.PCGames, "|- Старые игры (Стратегии)"); + caps.Categories.AddCategoryMapping(2225, NewznabStandardCategory.PCGames, "|- Старые игры (Приключения и квесты)"); + caps.Categories.AddCategoryMapping(2206, NewznabStandardCategory.PCGames, "|- Старые игры (Симуляторы)"); caps.Categories.AddCategoryMapping(2228, NewznabStandardCategory.PCGames, "|- IBM-PC-несовместимые компьютеры"); caps.Categories.AddCategoryMapping(139, NewznabStandardCategory.PCGames, "Прочее для Windows-игр"); caps.Categories.AddCategoryMapping(2478, NewznabStandardCategory.PCGames, "|- Официальные патчи, моды, плагины, дополнения"); @@ -1255,8 +1271,8 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(908, NewznabStandardCategory.Console, "|- PS"); caps.Categories.AddCategoryMapping(357, NewznabStandardCategory.ConsoleOther, "|- PS2"); caps.Categories.AddCategoryMapping(886, NewznabStandardCategory.ConsolePS3, "|- PS3"); - caps.Categories.AddCategoryMapping(546, NewznabStandardCategory.Console, "|- Игры PS1, PS2 и PSP для PS3"); caps.Categories.AddCategoryMapping(973, NewznabStandardCategory.ConsolePS4, "|- PS4"); + caps.Categories.AddCategoryMapping(546, NewznabStandardCategory.ConsoleOther, "|- PS5"); caps.Categories.AddCategoryMapping(1352, NewznabStandardCategory.ConsolePSP, "|- PSP"); caps.Categories.AddCategoryMapping(1116, NewznabStandardCategory.ConsolePSP, "|- Игры PS1 для PSP"); caps.Categories.AddCategoryMapping(595, NewznabStandardCategory.ConsolePSVita, "|- PS Vita"); @@ -1279,7 +1295,6 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(650, NewznabStandardCategory.PCMobileOther, "Игры для мобильных устройств"); caps.Categories.AddCategoryMapping(2149, NewznabStandardCategory.PCMobileAndroid, "|- Игры для Android"); caps.Categories.AddCategoryMapping(2420, NewznabStandardCategory.ConsoleOther, "|- Игры для Oculus Quest"); - caps.Categories.AddCategoryMapping(1001, NewznabStandardCategory.PC, "|- Игры для Java"); caps.Categories.AddCategoryMapping(1004, NewznabStandardCategory.PCMobileOther, "|- Игры для Symbian"); caps.Categories.AddCategoryMapping(1002, NewznabStandardCategory.PCMobileOther, "|- Игры для Windows Mobile"); caps.Categories.AddCategoryMapping(240, NewznabStandardCategory.OtherMisc, "Игровое видео"); @@ -1295,7 +1310,6 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(1379, NewznabStandardCategory.PC, "|- Операционные системы (Linux, Unix)"); caps.Categories.AddCategoryMapping(1381, NewznabStandardCategory.PC, "|- Программное обеспечение (Linux, Unix)"); caps.Categories.AddCategoryMapping(1473, NewznabStandardCategory.PC, "|- Другие ОС и ПО под них"); - caps.Categories.AddCategoryMapping(1195, NewznabStandardCategory.PC, "Тестовые диски для настройки аудио/видео аппаратуры"); caps.Categories.AddCategoryMapping(1013, NewznabStandardCategory.PC, "Системные программы"); caps.Categories.AddCategoryMapping(1028, NewznabStandardCategory.PC, "|- Работа с жёстким диском"); caps.Categories.AddCategoryMapping(1029, NewznabStandardCategory.PC, "|- Резервное копирование"); @@ -1306,15 +1320,12 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(1034, NewznabStandardCategory.PC, "|- Информация и диагностика"); caps.Categories.AddCategoryMapping(1066, NewznabStandardCategory.PC, "|- Программы для интернет и сетей"); caps.Categories.AddCategoryMapping(1035, NewznabStandardCategory.PC, "|- ПО для защиты компьютера (Антивирусное ПО, Фаерволлы)"); - caps.Categories.AddCategoryMapping(1038, NewznabStandardCategory.PC, "|- Анти-шпионы и анти-трояны"); - caps.Categories.AddCategoryMapping(1039, NewznabStandardCategory.PC, "|- Программы для защиты информации"); caps.Categories.AddCategoryMapping(1536, NewznabStandardCategory.PC, "|- Драйверы и прошивки"); caps.Categories.AddCategoryMapping(1051, NewznabStandardCategory.PC, "|- Оригинальные диски к компьютерам и комплектующим"); caps.Categories.AddCategoryMapping(1040, NewznabStandardCategory.PC, "|- Серверное ПО для Windows"); caps.Categories.AddCategoryMapping(1041, NewznabStandardCategory.PC, "|- Изменение интерфейса ОС Windows"); caps.Categories.AddCategoryMapping(1636, NewznabStandardCategory.PC, "|- Скринсейверы"); caps.Categories.AddCategoryMapping(1042, NewznabStandardCategory.PC, "|- Разное (Системные программы под Windows)"); - caps.Categories.AddCategoryMapping(1059, NewznabStandardCategory.PC, "|- Архив (Разрегистрированные раздачи)"); caps.Categories.AddCategoryMapping(1014, NewznabStandardCategory.PC, "Системы для бизнеса, офиса, научной и проектной работы"); caps.Categories.AddCategoryMapping(2134, NewznabStandardCategory.PC, "|- Медицина - интерактивный софт"); caps.Categories.AddCategoryMapping(1060, NewznabStandardCategory.PC, "|- Всё для дома: кройка, шитьё, кулинария"); @@ -1340,6 +1351,7 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(1018, NewznabStandardCategory.PC, "|- Шаблоны для сайтов и CMS"); caps.Categories.AddCategoryMapping(1058, NewznabStandardCategory.PC, "|- Разное (Веб-разработка и программирование)"); caps.Categories.AddCategoryMapping(1016, NewznabStandardCategory.PC, "Программы для работы с мультимедиа и 3D"); + caps.Categories.AddCategoryMapping(1195, NewznabStandardCategory.PC, "|- Тестовые диски для настройки аудио/видео аппаратуры"); caps.Categories.AddCategoryMapping(1079, NewznabStandardCategory.PC, "|- Программные комплекты"); caps.Categories.AddCategoryMapping(1080, NewznabStandardCategory.PC, "|- Плагины для программ компании Adobe"); caps.Categories.AddCategoryMapping(1081, NewznabStandardCategory.PC, "|- Графические редакторы"); @@ -1442,8 +1454,6 @@ namespace NzbDrone.Core.Indexers.Definitions caps.Categories.AddCategoryMapping(630, NewznabStandardCategory.OtherMisc, "|- Обои"); caps.Categories.AddCategoryMapping(1664, NewznabStandardCategory.OtherMisc, "|- Фото знаменитостей"); caps.Categories.AddCategoryMapping(148, NewznabStandardCategory.Audio, "|- Аудио"); - caps.Categories.AddCategoryMapping(965, NewznabStandardCategory.AudioMP3, "|- Музыка (lossy)"); - caps.Categories.AddCategoryMapping(134, NewznabStandardCategory.AudioLossless, "|- Музыка (lossless)"); caps.Categories.AddCategoryMapping(807, NewznabStandardCategory.TVOther, "|- Видео"); caps.Categories.AddCategoryMapping(147, NewznabStandardCategory.Books, "|- Публикации и учебные материалы (тексты)"); caps.Categories.AddCategoryMapping(847, NewznabStandardCategory.MoviesOther, "|- Трейлеры и дополнительные материалы к фильмам"); @@ -1742,7 +1752,7 @@ namespace NzbDrone.Core.Indexers.Definitions title = Regex.Replace(title, @"(\([\p{IsCyrillic}\W]+)\s/\s(.+?)\)", string.Empty, RegexOptions.Compiled | RegexOptions.IgnoreCase); // Remove VO, MVO and DVO from titles - var vo = new Regex(@".VO\s\(.+?\)"); + var vo = new Regex(@"((?:\dx\s)?(?:[A-Z])?VO\s\(.+?\))"); title = vo.Replace(title, string.Empty); // Remove R5 and (R5) from release names @@ -1750,7 +1760,7 @@ namespace NzbDrone.Core.Indexers.Definitions title = r5.Replace(title, "$1"); // Remove Sub languages from release names - title = Regex.Replace(title, @"(\bSub\b.*$|\b[\+]*Sub[\+]*\b)", string.Empty); + title = Regex.Replace(title, @"(\bSub\b[^+]*\b|\b[\+]*Sub[\+]*\b)", string.Empty); } // language fix: all rutracker releases contains russian track diff --git a/src/NzbDrone.Core/Indexers/Definitions/SecretCinema.cs b/src/NzbDrone.Core/Indexers/Definitions/SecretCinema.cs index 1950974c0..e8d8b1584 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/SecretCinema.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/SecretCinema.cs @@ -6,6 +6,7 @@ using System.Text.RegularExpressions; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; +using NzbDrone.Common.Serializer; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers.Definitions.Gazelle; using NzbDrone.Core.Indexers.Exceptions; @@ -19,7 +20,7 @@ public class SecretCinema : GazelleBase<GazelleSettings> { public override string Name => "Secret Cinema"; public override string[] IndexerUrls => new[] { "https://secret-cinema.pw/" }; - public override string Description => "A tracker for rare movies."; + public override string Description => "Secret Cinema is a Private ratioless site for rare MOVIES."; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); @@ -78,7 +79,9 @@ public class SecretCinemaParser : IParseIndexerResponse // Remove cookie cache CookiesUpdater(null, null); - throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request"); + STJson.TryDeserialize<GazelleErrorResponse>(indexerResponse.Content, out var errorResponse); + + throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request: {errorResponse?.Error ?? "Check the logs for more information."}"); } if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value)) @@ -148,7 +151,7 @@ public class SecretCinemaParser : IParseIndexerResponse if (torrent.RemasterTitle.IsNotNullOrWhiteSpace()) { - release.Title += $" [{torrent.RemasterTitle.Trim()}]"; + release.Title += $" [{WebUtility.HtmlDecode(torrent.RemasterTitle).Trim()}]"; } // Replace media formats with standards @@ -226,7 +229,7 @@ public class SecretCinemaParser : IParseIndexerResponse .AddQueryParam("action", "download") .AddQueryParam("id", torrentId); - if (_settings.UseFreeleechToken) + if (_settings.UseFreeleechToken is (int)GazelleFreeleechTokenAction.Preferred or (int)GazelleFreeleechTokenAction.Required) { url = url.AddQueryParam("useToken", "1"); } diff --git a/src/NzbDrone.Core/Indexers/Definitions/SpeedApp/SpeedAppBase.cs b/src/NzbDrone.Core/Indexers/Definitions/SpeedApp/SpeedAppBase.cs index f0170d868..244a9c286 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/SpeedApp/SpeedAppBase.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/SpeedApp/SpeedAppBase.cs @@ -262,7 +262,7 @@ namespace NzbDrone.Core.Indexers.Definitions return jsonResponse.Resource.Select(torrent => new TorrentInfo { - Guid = torrent.Id.ToString(), + Guid = torrent.Url, Title = CleanTitle(torrent.Name), Description = torrent.ShortDescription, Size = torrent.Size, diff --git a/src/NzbDrone.Core/Indexers/Definitions/SubsPlease.cs b/src/NzbDrone.Core/Indexers/Definitions/SubsPlease.cs index fe313997b..184a19f08 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/SubsPlease.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/SubsPlease.cs @@ -75,6 +75,8 @@ namespace NzbDrone.Core.Indexers.Definitions public class SubsPleaseRequestGenerator : IIndexerRequestGenerator { + private static readonly Regex ResolutionRegex = new (@"\d{3,4}p", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private readonly NoAuthTorrentBaseSettings _settings; public SubsPleaseRequestGenerator(NoAuthTorrentBaseSettings settings) @@ -134,15 +136,6 @@ namespace NzbDrone.Core.Indexers.Definitions private IEnumerable<IndexerRequest> GetSearchRequests(string term, SearchCriteriaBase searchCriteria) { - var searchTerm = Regex.Replace(term, "\\[?SubsPlease\\]?\\s*", string.Empty, RegexOptions.IgnoreCase).Trim(); - - // If the search terms contain a resolution, remove it from the query sent to the API - var resMatch = Regex.Match(searchTerm, "\\d{3,4}[p|P]"); - if (resMatch.Success) - { - searchTerm = searchTerm.Replace(resMatch.Value, string.Empty).Trim(); - } - var queryParameters = new NameValueCollection { { "tz", "UTC" } @@ -154,6 +147,16 @@ namespace NzbDrone.Core.Indexers.Definitions } else { + var searchTerm = Regex.Replace(term, "\\[?SubsPlease\\]?\\s*", string.Empty, RegexOptions.IgnoreCase).Trim(); + + // If the search terms contain a resolution, remove it from the query sent to the API + var resolutionMatch = ResolutionRegex.Match(searchTerm); + + if (resolutionMatch.Success) + { + searchTerm = searchTerm.Replace(resolutionMatch.Value, string.Empty).Trim(); + } + queryParameters.Set("f", "search"); queryParameters.Set("s", searchTerm); } @@ -201,7 +204,7 @@ namespace NzbDrone.Core.Indexers.Definitions { var release = new TorrentInfo { - InfoUrl = _settings.BaseUrl + $"shows/{value.Page}/", + InfoUrl = $"{_settings.BaseUrl}shows/{value.Page}/", PublishDate = value.ReleaseDate.LocalDateTime, Files = 1, Categories = new List<IndexerCategory> { NewznabStandardCategory.TVAnime }, @@ -213,13 +216,18 @@ namespace NzbDrone.Core.Indexers.Definitions UploadVolumeFactor = 1 }; + if (value.ImageUrl.IsNotNullOrWhiteSpace()) + { + release.PosterUrl = _settings.BaseUrl + value.ImageUrl.TrimStart('/'); + } + if (value.Episode.ToLowerInvariant() == "movie") { release.Categories.Add(NewznabStandardCategory.MoviesOther); } // Ex: [SubsPlease] Shingeki no Kyojin (The Final Season) - 64 (1080p) - release.Title += $"[SubsPlease] {value.Show} - {value.Episode} ({d.Resolution}p)"; + release.Title = $"[SubsPlease] {value.Show} - {value.Episode} ({d.Resolution}p)"; release.MagnetUrl = d.Magnet; release.DownloadUrl = null; release.Guid = d.Magnet; @@ -269,6 +277,8 @@ namespace NzbDrone.Core.Indexers.Definitions public string Episode { get; set; } public SubPleaseDownloadInfo[] Downloads { get; set; } public string Xdcc { get; set; } + + [JsonProperty("image_url")] public string ImageUrl { get; set; } public string Page { get; set; } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentDay.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentDay.cs index 9371c8aa0..aa02c4578 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentDay.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentDay.cs @@ -7,6 +7,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Annotations; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Indexers.Exceptions; using NzbDrone.Core.Indexers.Settings; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Messaging.Events; @@ -52,7 +53,7 @@ namespace NzbDrone.Core.Indexers.Definitions public override IParseIndexerResponse GetParser() { - return new TorrentDayParser(Settings, Capabilities.Categories); + return new TorrentDayParser(Settings, Capabilities.Categories, _logger); } protected override IDictionary<string, string> GetCookies() @@ -228,15 +229,29 @@ namespace NzbDrone.Core.Indexers.Definitions { private readonly TorrentDaySettings _settings; private readonly IndexerCapabilitiesCategories _categories; + private readonly Logger _logger; - public TorrentDayParser(TorrentDaySettings settings, IndexerCapabilitiesCategories categories) + public TorrentDayParser(TorrentDaySettings settings, IndexerCapabilitiesCategories categories, Logger logger) { _settings = settings; _categories = categories; + _logger = logger; } public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse) { + if (indexerResponse.HttpResponse.HasHttpRedirect) + { + _logger.Warn("Redirected to {0} from indexer request", indexerResponse.HttpResponse.RedirectUrl); + + if (indexerResponse.HttpResponse.RedirectUrl.ContainsIgnoreCase("/login.php")) + { + throw new IndexerException(indexerResponse, "We are being redirected to the login page. Most likely your session expired or was killed. Recheck your cookie and try testing the indexer."); + } + + throw new IndexerException(indexerResponse, "Redirected to {0} from indexer request", indexerResponse.HttpResponse.RedirectUrl); + } + var torrentInfos = new List<TorrentInfo>(); var rows = JsonConvert.DeserializeObject<dynamic>(indexerResponse.Content); diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexer.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexer.cs index 52af639b0..c2afa00d2 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexer.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexer.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using System.Threading.Tasks; +using FluentValidation.Results; using NLog; using NzbDrone.Core.Configuration; using NzbDrone.Core.Messaging.Events; @@ -44,6 +46,13 @@ namespace NzbDrone.Core.Indexers.Definitions.TorrentRss } } + protected override Task<ValidationFailure> TestConnection() + { + UpdateCookies(null, null); + + return base.TestConnection(); + } + private IndexerDefinition GetDefinition(string name, string description, TorrentRssIndexerSettings settings) { return new IndexerDefinition diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentsCSV.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentsCSV.cs index ddb868ccc..80835a3ec 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentsCSV.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentsCSV.cs @@ -3,10 +3,11 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Text; -using Newtonsoft.Json.Linq; +using System.Text.Json.Serialization; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; +using NzbDrone.Common.Serializer; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers.Settings; using NzbDrone.Core.IndexerSearch.Definitions; @@ -145,32 +146,31 @@ namespace NzbDrone.Core.Indexers.Definitions { var releaseInfos = new List<ReleaseInfo>(); - var jsonContent = JArray.Parse(indexerResponse.Content); + var jsonResponse = STJson.Deserialize<TorrentsCSVResponse>(indexerResponse.Content); - foreach (var torrent in jsonContent) + foreach (var torrent in jsonResponse.Torrents) { if (torrent == null) { continue; } - var infoHash = torrent.Value<string>("infohash"); - var title = torrent.Value<string>("name"); - var size = torrent.Value<long>("size_bytes"); - var seeders = torrent.Value<int?>("seeders") ?? 0; - var leechers = torrent.Value<int?>("leechers") ?? 0; - var grabs = torrent.Value<int?>("completed") ?? 0; - var publishDate = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(torrent.Value<long>("created_unix")); + var infoHash = torrent.InfoHash; + var title = torrent.Name; + var seeders = torrent.Seeders ?? 0; + var leechers = torrent.Leechers ?? 0; + var grabs = torrent.Completed ?? 0; + var publishDate = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(torrent.Created); var release = new TorrentInfo { - Title = title, - InfoUrl = $"{_settings.BaseUrl.TrimEnd('/')}/search/{title}", // there is no details link Guid = $"magnet:?xt=urn:btih:{infoHash}", + InfoUrl = $"{_settings.BaseUrl.TrimEnd('/')}/search?q={title}", // there is no details link + Title = title, InfoHash = infoHash, // magnet link is auto generated from infohash Categories = new List<IndexerCategory> { NewznabStandardCategory.Other }, PublishDate = publishDate, - Size = size, + Size = torrent.Size, Grabs = grabs, Seeders = seeders, Peers = leechers + seeders, @@ -188,4 +188,29 @@ namespace NzbDrone.Core.Indexers.Definitions public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; } } + + public class TorrentsCSVResponse + { + public IReadOnlyCollection<TorrentsCSVTorrent> Torrents { get; set; } + } + + public class TorrentsCSVTorrent + { + [JsonPropertyName("infohash")] + public string InfoHash { get; set; } + + public string Name { get; set; } + + [JsonPropertyName("size_bytes")] + public long Size { get; set; } + + [JsonPropertyName("created_unix")] + public long Created { get; set; } + + public int? Leechers { get; set; } + + public int? Seeders { get; set; } + + public int? Completed { get; set; } + } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Torznab/TorznabRssParser.cs b/src/NzbDrone.Core/Indexers/Definitions/Torznab/TorznabRssParser.cs index 04c67c376..48ebe4846 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Torznab/TorznabRssParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Torznab/TorznabRssParser.cs @@ -100,16 +100,17 @@ namespace NzbDrone.Core.Indexers.Torznab protected override bool PostProcess(IndexerResponse indexerResponse, List<XElement> items, List<ReleaseInfo> releases) { var enclosureTypes = items.SelectMany(GetEnclosures).Select(v => v.Type).Distinct().ToArray(); + if (enclosureTypes.Any() && enclosureTypes.Intersect(PreferredEnclosureMimeTypes).Empty()) { if (enclosureTypes.Intersect(UsenetEnclosureMimeTypes).Any()) { - _logger.Warn("Feed does not contain {0}, found {1}, did you intend to add a Newznab indexer?", TorrentEnclosureMimeType, enclosureTypes[0]); - } - else - { - _logger.Warn("Feed does not contain {0}, found {1}.", TorrentEnclosureMimeType, enclosureTypes[0]); + _logger.Warn("{0} does not contain {1}, found {2}, did you intend to add a Newznab indexer?", indexerResponse.Request.Url, TorrentEnclosureMimeType, enclosureTypes[0]); + + return false; } + + _logger.Warn("{0} does not contain {1}, found {2}.", indexerResponse.Request.Url, TorrentEnclosureMimeType, enclosureTypes[0]); } return true; diff --git a/src/NzbDrone.Core/Indexers/Definitions/Uniotaku.cs b/src/NzbDrone.Core/Indexers/Definitions/Uniotaku.cs index 9405e63c3..12eca92f6 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Uniotaku.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Uniotaku.cs @@ -82,7 +82,7 @@ public class Uniotaku : TorrentIndexerBase<UniotakuSettings> return !httpResponse.GetCookies().ContainsKey("uid") || !httpResponse.GetCookies().ContainsKey("pass"); } - public override async Task<byte[]> Download(Uri link) + public override async Task<IndexerDownloadResponse> Download(Uri link) { var request = new HttpRequestBuilder(link.ToString()) .SetCookies(GetCookies() ?? new Dictionary<string, string>()) @@ -137,6 +137,7 @@ public class Uniotaku : TorrentIndexerBase<UniotakuSettings> caps.Categories.AddCategoryMapping(55, NewznabStandardCategory.XXX, "Hentai"); caps.Categories.AddCategoryMapping(56, NewznabStandardCategory.XXXOther, "H Doujinshi"); caps.Categories.AddCategoryMapping(57, NewznabStandardCategory.TVOther, "Tokusatsu"); + caps.Categories.AddCategoryMapping(58, NewznabStandardCategory.TVOther, "Live Action"); return caps; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/XSpeeds.cs b/src/NzbDrone.Core/Indexers/Definitions/XSpeeds.cs index 381441434..72d4f1c2b 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/XSpeeds.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/XSpeeds.cs @@ -121,8 +121,15 @@ public class XSpeeds : TorrentIndexerBase<XSpeedsSettings> caps.Categories.AddCategoryMapping(112, NewznabStandardCategory.MoviesOther, "Anime Movies"); caps.Categories.AddCategoryMapping(111, NewznabStandardCategory.MoviesOther, "Anime TV"); caps.Categories.AddCategoryMapping(150, NewznabStandardCategory.PC, "Apps"); - caps.Categories.AddCategoryMapping(80, NewznabStandardCategory.AudioAudiobook, "Audiobooks"); - caps.Categories.AddCategoryMapping(48, NewznabStandardCategory.Books, "Books Magazines"); + caps.Categories.AddCategoryMapping(156, NewznabStandardCategory.TV, "AV1"); + caps.Categories.AddCategoryMapping(156, NewznabStandardCategory.Movies, "AV1"); + caps.Categories.AddCategoryMapping(159, NewznabStandardCategory.Movies, "Movie Boxsets AV1"); + caps.Categories.AddCategoryMapping(158, NewznabStandardCategory.Movies, "Movies AV1"); + caps.Categories.AddCategoryMapping(157, NewznabStandardCategory.TV, "TV AV1"); + caps.Categories.AddCategoryMapping(160, NewznabStandardCategory.TV, "TV Boxsets AV1"); + caps.Categories.AddCategoryMapping(153, NewznabStandardCategory.Books, "Books"); + caps.Categories.AddCategoryMapping(154, NewznabStandardCategory.AudioAudiobook, "Audiobooks"); + caps.Categories.AddCategoryMapping(155, NewznabStandardCategory.Books, "Books & Magazines"); caps.Categories.AddCategoryMapping(68, NewznabStandardCategory.MoviesOther, "Cams/TS"); caps.Categories.AddCategoryMapping(140, NewznabStandardCategory.TVDocumentary, "Documentary"); caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.MoviesDVD, "DVDR"); @@ -154,6 +161,7 @@ public class XSpeeds : TorrentIndexerBase<XSpeedsSettings> caps.Categories.AddCategoryMapping(146, NewznabStandardCategory.MoviesSD, "Movies SD"); caps.Categories.AddCategoryMapping(13, NewznabStandardCategory.Audio, "Music"); caps.Categories.AddCategoryMapping(135, NewznabStandardCategory.AudioLossless, "Music/FLAC"); + caps.Categories.AddCategoryMapping(151, NewznabStandardCategory.Audio, "Karaoke"); caps.Categories.AddCategoryMapping(136, NewznabStandardCategory.Audio, "Music Boxset"); caps.Categories.AddCategoryMapping(148, NewznabStandardCategory.AudioVideo, "Music Videos"); caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.Other, "Other"); diff --git a/src/NzbDrone.Core/Indexers/Events/IndexerAuthEvent.cs b/src/NzbDrone.Core/Indexers/Events/IndexerAuthEvent.cs index ed515b3b0..9a8888ae1 100644 --- a/src/NzbDrone.Core/Indexers/Events/IndexerAuthEvent.cs +++ b/src/NzbDrone.Core/Indexers/Events/IndexerAuthEvent.cs @@ -6,13 +6,13 @@ namespace NzbDrone.Core.Indexers.Events { public int IndexerId { get; set; } public bool Successful { get; set; } - public long Time { get; set; } + public long ElapsedTime { get; set; } - public IndexerAuthEvent(int indexerId, bool successful, long time) + public IndexerAuthEvent(int indexerId, bool successful, long elapsedTime) { IndexerId = indexerId; Successful = successful; - Time = time; + ElapsedTime = elapsedTime; } } } diff --git a/src/NzbDrone.Core/Indexers/Events/IndexerDownloadEvent.cs b/src/NzbDrone.Core/Indexers/Events/IndexerDownloadEvent.cs index 43bf8cd60..d68397562 100644 --- a/src/NzbDrone.Core/Indexers/Events/IndexerDownloadEvent.cs +++ b/src/NzbDrone.Core/Indexers/Events/IndexerDownloadEvent.cs @@ -18,6 +18,7 @@ namespace NzbDrone.Core.Indexers.Events public string DownloadId { get; set; } public IIndexer Indexer { get; set; } public GrabTrigger GrabTrigger { get; set; } + public long ElapsedTime { get; set; } public IndexerDownloadEvent(ReleaseInfo release, bool successful, string source, string host, string title, string url) { diff --git a/src/NzbDrone.Core/Indexers/Exceptions/IndexerException.cs b/src/NzbDrone.Core/Indexers/Exceptions/IndexerException.cs index 629f596a8..f0f1c2899 100644 --- a/src/NzbDrone.Core/Indexers/Exceptions/IndexerException.cs +++ b/src/NzbDrone.Core/Indexers/Exceptions/IndexerException.cs @@ -1,23 +1,34 @@ -using NzbDrone.Common.Exceptions; +using System; +using NzbDrone.Common.Exceptions; namespace NzbDrone.Core.Indexers.Exceptions { public class IndexerException : NzbDroneException { - private readonly IndexerResponse _indexerResponse; - - public IndexerException(IndexerResponse response, string message, params object[] args) - : base(message, args) - { - _indexerResponse = response; - } + public IndexerResponse Response { get; } public IndexerException(IndexerResponse response, string message) : base(message) { - _indexerResponse = response; + Response = response; } - public IndexerResponse Response => _indexerResponse; + public IndexerException(IndexerResponse response, string message, Exception innerException) + : base(message, innerException) + { + Response = response; + } + + public IndexerException(IndexerResponse response, string message, params object[] args) + : base(message, args) + { + Response = response; + } + + public IndexerException(IndexerResponse response, string message, Exception innerException, params object[] args) + : base(message, innerException, args) + { + Response = response; + } } } diff --git a/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs b/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs index 26d1bae96..4a8014c63 100644 --- a/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs @@ -18,6 +18,7 @@ using NzbDrone.Core.Indexers.Exceptions; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Validation; using Polly; using Polly.Retry; @@ -223,7 +224,7 @@ namespace NzbDrone.Core.Indexers return FetchReleases(g => SetCookieFunctions(g).GetSearchRequests(searchCriteria), searchCriteria); } - public override async Task<byte[]> Download(Uri link) + public override async Task<IndexerDownloadResponse> Download(Uri link) { Cookies = GetCookies(); @@ -232,7 +233,7 @@ namespace NzbDrone.Core.Indexers if (request.Url.Scheme == "magnet") { ValidateMagnet(request.Url.FullUri); - return Encoding.UTF8.GetBytes(request.Url.FullUri); + return new IndexerDownloadResponse(Encoding.UTF8.GetBytes(request.Url.FullUri)); } if (request.RateLimit < RateLimit) @@ -243,12 +244,17 @@ namespace NzbDrone.Core.Indexers request.AllowAutoRedirect = false; byte[] fileData; + long elapsedTime; try { var response = await _httpClient.ExecuteProxiedAsync(request, Definition); - if (response.StatusCode is HttpStatusCode.MovedPermanently or HttpStatusCode.Found or HttpStatusCode.SeeOther) + if (response.StatusCode is HttpStatusCode.MovedPermanently + or HttpStatusCode.Found + or HttpStatusCode.SeeOther + or HttpStatusCode.TemporaryRedirect + or HttpStatusCode.PermanentRedirect) { var autoRedirectChain = new List<string> { request.Url.ToString() }; @@ -282,6 +288,7 @@ namespace NzbDrone.Core.Indexers } fileData = response.ResponseData; + elapsedTime = response.ElapsedTime; _logger.Debug("Downloaded for release finished ({0} bytes from {1})", fileData.Length, link.AbsoluteUri); } @@ -319,7 +326,7 @@ namespace NzbDrone.Core.Indexers ValidateDownloadData(fileData); - return fileData; + return new IndexerDownloadResponse(fileData, elapsedTime); } protected virtual Task<HttpRequest> GetDownloadRequest(Uri link) @@ -604,12 +611,7 @@ namespace NzbDrone.Core.Indexers protected virtual bool CheckIfLoginNeeded(HttpResponse httpResponse) { - if (httpResponse.StatusCode == HttpStatusCode.Unauthorized) - { - return true; - } - - return false; + return httpResponse.StatusCode == HttpStatusCode.Unauthorized; } protected virtual Task DoLogin() @@ -652,7 +654,7 @@ namespace NzbDrone.Core.Indexers { foreach (var cookie in Cookies) { - request.HttpRequest.Cookies.Add(cookie.Key, cookie.Value); + request.HttpRequest.Cookies[cookie.Key] = cookie.Value; } } @@ -763,7 +765,7 @@ namespace NzbDrone.Core.Indexers if (releases.Releases.Empty()) { - return new ValidationFailure(string.Empty, "Query successful, but no results were returned from your indexer. This may be an issue with the indexer, your indexer category settings, or other indexer settings such as search freeleech only etc."); + return new ValidationFailure(string.Empty, "Query successful, but no results were returned from your indexer. This may be an issue with the indexer, your indexer category settings, or other indexer settings such as search freeleech only etc. See the FAQ for details."); } } catch (IndexerAuthException ex) @@ -814,7 +816,10 @@ namespace NzbDrone.Core.Indexers { _logger.Warn(ex, "Unable to connect to indexer"); - return new ValidationFailure(string.Empty, "Unable to connect to indexer, please check your DNS settings and ensure IPv6 is working or disabled. " + ex.Message); + return new NzbDroneValidationFailure(string.Empty, "Unable to connect to indexer, please check your DNS settings and ensure IPv6 is working or disabled. " + ex.Message) + { + DetailedDescription = ex.InnerException?.Message + }; } catch (TaskCanceledException ex) { @@ -841,7 +846,10 @@ namespace NzbDrone.Core.Indexers { _logger.Warn(ex, "Unable to connect to indexer"); - return new ValidationFailure(string.Empty, "Unable to connect to indexer, check the log above the ValidationFailure for more details. " + ex.Message); + return new NzbDroneValidationFailure(string.Empty, "Unable to connect to indexer, check the log above the ValidationFailure for more details. " + ex.Message) + { + DetailedDescription = ex.InnerException?.Message + }; } return null; diff --git a/src/NzbDrone.Core/Indexers/IIndexer.cs b/src/NzbDrone.Core/Indexers/IIndexer.cs index 25d12db9a..e0a6b1ef1 100644 --- a/src/NzbDrone.Core/Indexers/IIndexer.cs +++ b/src/NzbDrone.Core/Indexers/IIndexer.cs @@ -28,7 +28,7 @@ namespace NzbDrone.Core.Indexers Task<IndexerPageableQueryResult> Fetch(BookSearchCriteria searchCriteria); Task<IndexerPageableQueryResult> Fetch(BasicSearchCriteria searchCriteria); - Task<byte[]> Download(Uri link); + Task<IndexerDownloadResponse> Download(Uri link); bool IsObsolete(); IndexerCapabilities GetCapabilities(); diff --git a/src/NzbDrone.Core/Indexers/IndexerBase.cs b/src/NzbDrone.Core/Indexers/IndexerBase.cs index 1caed03b3..0330217dc 100644 --- a/src/NzbDrone.Core/Indexers/IndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/IndexerBase.cs @@ -97,7 +97,7 @@ namespace NzbDrone.Core.Indexers public abstract Task<IndexerPageableQueryResult> Fetch(TvSearchCriteria searchCriteria); public abstract Task<IndexerPageableQueryResult> Fetch(BookSearchCriteria searchCriteria); public abstract Task<IndexerPageableQueryResult> Fetch(BasicSearchCriteria searchCriteria); - public abstract Task<byte[]> Download(Uri link); + public abstract Task<IndexerDownloadResponse> Download(Uri link); public abstract IndexerCapabilities GetCapabilities(); diff --git a/src/NzbDrone.Core/Indexers/IndexerDownloadResponse.cs b/src/NzbDrone.Core/Indexers/IndexerDownloadResponse.cs new file mode 100644 index 000000000..a5660a784 --- /dev/null +++ b/src/NzbDrone.Core/Indexers/IndexerDownloadResponse.cs @@ -0,0 +1,13 @@ +namespace NzbDrone.Core.Indexers; + +public class IndexerDownloadResponse +{ + public byte[] Data { get; private set; } + public long ElapsedTime { get; private set; } + + public IndexerDownloadResponse(byte[] data, long elapsedTime = 0) + { + Data = data; + ElapsedTime = elapsedTime; + } +} diff --git a/src/NzbDrone.Core/Indexers/IndexerFactory.cs b/src/NzbDrone.Core/Indexers/IndexerFactory.cs index c67c7840e..46a7e8c3e 100644 --- a/src/NzbDrone.Core/Indexers/IndexerFactory.cs +++ b/src/NzbDrone.Core/Indexers/IndexerFactory.cs @@ -242,7 +242,7 @@ namespace NzbDrone.Core.Indexers foreach (var indexer in indexers) { - if (blockedIndexers.TryGetValue(indexer.Definition.Id, out var blockedIndexerStatus)) + if (blockedIndexers.TryGetValue(indexer.Definition.Id, out var blockedIndexerStatus) && blockedIndexerStatus.DisabledTill.HasValue) { _logger.Debug("Temporarily ignoring indexer {0} till {1} due to recent failures.", indexer.Definition.Name, blockedIndexerStatus.DisabledTill.Value.ToLocalTime()); continue; @@ -318,5 +318,24 @@ namespace NzbDrone.Core.Indexers base.Update(definition); } + + public override IEnumerable<IndexerDefinition> Update(IEnumerable<IndexerDefinition> definitions) + { + var indexerDefinitions = definitions.ToList(); + + foreach (var definition in indexerDefinitions) + { + var provider = _providers.First(v => v.GetType().Name == definition.Implementation); + + SetProviderCharacteristics(provider, definition); + + if (definition.Implementation == nameof(Cardigann)) + { + MapCardigannDefinition(definition); + } + } + + return base.Update(indexerDefinitions); + } } } diff --git a/src/NzbDrone.Core/Indexers/IndexerFlag.cs b/src/NzbDrone.Core/Indexers/IndexerFlag.cs index 26a65e96c..cfa3d35de 100644 --- a/src/NzbDrone.Core/Indexers/IndexerFlag.cs +++ b/src/NzbDrone.Core/Indexers/IndexerFlag.cs @@ -63,6 +63,7 @@ namespace NzbDrone.Core.Indexers } public static IndexerFlag Internal => new ("internal", "Uploader is an internal release group"); + public static IndexerFlag Exclusive => new ("exclusive", "An exclusive release that must not be uploaded anywhere else"); public static IndexerFlag FreeLeech => new ("freeleech", "Download doesn't count toward ratio"); public static IndexerFlag NeutralLeech => new ("neutralleech", "Download and upload doesn't count toward ratio"); public static IndexerFlag HalfLeech => new ("halfleech", "Release counts 50% to ratio"); diff --git a/src/NzbDrone.Core/Indexers/IndexerTorrentBaseSettings.cs b/src/NzbDrone.Core/Indexers/IndexerTorrentBaseSettings.cs index 9e07cbb42..7cdf4b311 100644 --- a/src/NzbDrone.Core/Indexers/IndexerTorrentBaseSettings.cs +++ b/src/NzbDrone.Core/Indexers/IndexerTorrentBaseSettings.cs @@ -63,5 +63,8 @@ namespace NzbDrone.Core.Indexers [FieldDefinition(4, Type = FieldType.Number, Label = "IndexerSettingsPackSeedTime", HelpText = "IndexerSettingsPackSeedTimeIndexerHelpText", Unit = "minutes", Advanced = true)] public int? PackSeedTime { get; set; } + + [FieldDefinition(5, Type = FieldType.Checkbox, Label = "IndexerSettingsPreferMagnetUrl", HelpText = "IndexerSettingsPreferMagnetUrlHelpText", Advanced = true)] + public bool PreferMagnetUrl { get; set; } } } diff --git a/src/NzbDrone.Core/Indexers/RssParser.cs b/src/NzbDrone.Core/Indexers/RssParser.cs index 51f7e8ae8..a9fd4ebeb 100644 --- a/src/NzbDrone.Core/Indexers/RssParser.cs +++ b/src/NzbDrone.Core/Indexers/RssParser.cs @@ -290,9 +290,9 @@ namespace NzbDrone.Core.Indexers Length = v.Attribute("length")?.Value?.ParseInt64() ?? 0 }; } - catch (Exception e) + catch (Exception ex) { - _logger.Warn(e, "Failed to get enclosure for: {0}", item.Title()); + _logger.Warn(ex, "Failed to get enclosure for: {0}", item.Title()); } return null; diff --git a/src/NzbDrone.Core/Indexers/TorrentIndexerBase.cs b/src/NzbDrone.Core/Indexers/TorrentIndexerBase.cs index 242fc802a..2dc9ae63f 100644 --- a/src/NzbDrone.Core/Indexers/TorrentIndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/TorrentIndexerBase.cs @@ -1,3 +1,4 @@ +using System; using System.Text; using MonoTorrent; using NLog; @@ -22,10 +23,10 @@ namespace NzbDrone.Core.Indexers { Torrent.Load(fileData); } - catch + catch (Exception ex) { - _logger.Info("Invalid torrent file contents: {0}", Encoding.ASCII.GetString(fileData)); - throw; + _logger.Debug("Invalid torrent file contents: {0}", Encoding.ASCII.GetString(fileData)); + throw new NotSupportedException($"Invalid torrent file contents. Reason: {ex.Message}", ex); } } } diff --git a/src/NzbDrone.Core/Instrumentation/DatabaseTarget.cs b/src/NzbDrone.Core/Instrumentation/DatabaseTarget.cs index 896b274a6..1c334a19c 100644 --- a/src/NzbDrone.Core/Instrumentation/DatabaseTarget.cs +++ b/src/NzbDrone.Core/Instrumentation/DatabaseTarget.cs @@ -60,33 +60,36 @@ namespace NzbDrone.Core.Instrumentation { try { - var log = new Log(); - log.Time = logEvent.TimeStamp; - log.Message = CleanseLogMessage.Cleanse(logEvent.FormattedMessage); - - log.Logger = logEvent.LoggerName; + var log = new Log + { + Time = logEvent.TimeStamp, + Logger = logEvent.LoggerName, + Level = logEvent.Level.Name + }; if (log.Logger.StartsWith("NzbDrone.")) { log.Logger = log.Logger.Remove(0, 9); } + var message = logEvent.FormattedMessage; + if (logEvent.Exception != null) { - if (string.IsNullOrWhiteSpace(log.Message)) + if (string.IsNullOrWhiteSpace(message)) { - log.Message = logEvent.Exception.Message; + message = logEvent.Exception.Message; } else { - log.Message += ": " + logEvent.Exception.Message; + message += ": " + logEvent.Exception.Message; } - log.Exception = logEvent.Exception.ToString(); + log.Exception = CleanseLogMessage.Cleanse(logEvent.Exception.ToString()); log.ExceptionType = logEvent.Exception.GetType().ToString(); } - log.Level = logEvent.Level.Name; + log.Message = CleanseLogMessage.Cleanse(message); var connectionInfo = _connectionStringFactory.LogDbConnection; diff --git a/src/NzbDrone.Core/Instrumentation/ReconfigureLogging.cs b/src/NzbDrone.Core/Instrumentation/ReconfigureLogging.cs index 38b695ccc..ac0cd085b 100644 --- a/src/NzbDrone.Core/Instrumentation/ReconfigureLogging.cs +++ b/src/NzbDrone.Core/Instrumentation/ReconfigureLogging.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using NLog; using NLog.Config; +using NLog.Targets; using NLog.Targets.Syslog; using NLog.Targets.Syslog.Settings; using NzbDrone.Common.EnvironmentInfo; @@ -50,14 +51,15 @@ namespace NzbDrone.Core.Instrumentation var rules = LogManager.Configuration.LoggingRules; - //Console + // Console + ReconfigureConsole(); SetMinimumLogLevel(rules, "consoleLogger", minimumConsoleLogLevel); //Log Files SetMinimumLogLevel(rules, "appFileInfo", minimumLogLevel <= LogLevel.Info ? LogLevel.Info : LogLevel.Off); SetMinimumLogLevel(rules, "appFileDebug", minimumLogLevel <= LogLevel.Debug ? LogLevel.Debug : LogLevel.Off); SetMinimumLogLevel(rules, "appFileTrace", minimumLogLevel <= LogLevel.Trace ? LogLevel.Trace : LogLevel.Off); - SetLogRotation(); + ReconfigureFile(); //Log Sql SqlBuilderExtensions.LogSql = _configFileProvider.LogSql; @@ -91,11 +93,12 @@ namespace NzbDrone.Core.Instrumentation } } - private void SetLogRotation() + private void ReconfigureFile() { - foreach (var target in LogManager.Configuration.AllTargets.OfType<NzbDroneFileTarget>()) + foreach (var target in LogManager.Configuration.AllTargets.OfType<CleansingFileTarget>()) { target.MaxArchiveFiles = _configFileProvider.LogRotate; + target.ArchiveAboveSize = _configFileProvider.LogSizeLimit.Megabytes(); } } @@ -109,6 +112,18 @@ namespace NzbDrone.Core.Instrumentation } } + private void ReconfigureConsole() + { + var consoleTarget = LogManager.Configuration.AllTargets.OfType<ColoredConsoleTarget>().FirstOrDefault(); + + if (consoleTarget != null) + { + var format = _configFileProvider.ConsoleLogFormat; + + NzbDroneLogger.ConfigureConsoleLayout(consoleTarget, format); + } + } + private void SetSyslogParameters(string syslogServer, int syslogPort, LogLevel minimumLogLevel) { var syslogTarget = new SyslogTarget(); @@ -117,6 +132,7 @@ namespace NzbDrone.Core.Instrumentation syslogTarget.MessageSend.Protocol = ProtocolType.Udp; syslogTarget.MessageSend.Udp.Port = syslogPort; syslogTarget.MessageSend.Udp.Server = syslogServer; + syslogTarget.MessageSend.Retry.ConstantBackoff.BaseDelay = 500; syslogTarget.MessageCreation.Rfc = RfcNumber.Rfc5424; syslogTarget.MessageCreation.Rfc5424.AppName = _configFileProvider.InstanceName; diff --git a/src/NzbDrone.Core/Jobs/TaskManager.cs b/src/NzbDrone.Core/Jobs/TaskManager.cs index f4b081b30..c6cff60ad 100644 --- a/src/NzbDrone.Core/Jobs/TaskManager.cs +++ b/src/NzbDrone.Core/Jobs/TaskManager.cs @@ -72,7 +72,7 @@ namespace NzbDrone.Core.Jobs new ScheduledTask { Interval = 6 * 60, - TypeName = typeof(ApplicationCheckUpdateCommand).FullName + TypeName = typeof(ApplicationUpdateCheckCommand).FullName }, new ScheduledTask diff --git a/src/NzbDrone.Core/Localization/Core/ar.json b/src/NzbDrone.Core/Localization/Core/ar.json index 17abab8e4..3cd2a3687 100644 --- a/src/NzbDrone.Core/Localization/Core/ar.json +++ b/src/NzbDrone.Core/Localization/Core/ar.json @@ -59,14 +59,14 @@ "SelectAll": "اختر الكل", "SendAnonymousUsageData": "إرسال بيانات الاستخدام المجهولة", "Style": "أسلوب", - "SystemTimeCheckMessage": "توقف وقت النظام بأكثر من يوم واحد. قد لا تعمل المهام المجدولة بشكل صحيح حتى يتم تصحيح الوقت", + "SystemTimeHealthCheckMessage": "توقف وقت النظام بأكثر من يوم واحد. قد لا تعمل المهام المجدولة بشكل صحيح حتى يتم تصحيح الوقت", "TableOptionsColumnsMessage": "اختر الأعمدة المرئية والترتيب الذي تظهر به", "TagCannotBeDeletedWhileInUse": "لا يمكن حذفه أثناء الاستخدام", "Tasks": "مهام", "UnableToAddANewAppProfilePleaseTryAgain": "غير قادر على إضافة ملف تعريف جودة جديد ، يرجى المحاولة مرة أخرى.", "UnableToAddANewDownloadClientPleaseTryAgain": "غير قادر على إضافة عميل تنزيل جديد ، يرجى المحاولة مرة أخرى.", "UnableToAddANewIndexerPleaseTryAgain": "غير قادر على إضافة مفهرس جديد ، يرجى المحاولة مرة أخرى.", - "UnableToLoadBackups": "تعذر تحميل النسخ الاحتياطية", + "BackupsLoadError": "تعذر تحميل النسخ الاحتياطية", "UnsavedChanges": "التغييرات غير المحفوظة", "UpdateUiNotWritableHealthCheckMessage": "لا يمكن تثبيت التحديث لأن مجلد واجهة المستخدم '{uiFolder}' غير قابل للكتابة بواسطة المستخدم '{userName}'", "UpdateScriptPathHelpText": "المسار إلى برنامج نصي مخصص يأخذ حزمة تحديث مستخرجة ويتعامل مع ما تبقى من عملية التحديث", @@ -124,7 +124,7 @@ "FocusSearchBox": "التركيز على مربع البحث", "Folder": "مجلد", "Health": "الصحة", - "HealthNoIssues": "لا مشاكل مع التكوين الخاص بك", + "NoIssuesWithYourConfiguration": "لا مشاكل مع التكوين الخاص بك", "Host": "مضيف", "IllRestartLater": "سأعيد التشغيل لاحقًا", "IncludeHealthWarningsHelpText": "قم بتضمين التحذيرات الصحية", @@ -329,7 +329,7 @@ "Queued": "في قائمة الانتظار", "Remove": "إزالة", "Replace": "يحل محل", - "TheLatestVersionIsAlreadyInstalled": "تم بالفعل تثبيت أحدث إصدار من {0}", + "OnLatestVersion": "تم بالفعل تثبيت أحدث إصدار من {0}", "DownloadClientPriorityHelpText": "تحديد أولويات عملاء التنزيل المتعددين. يتم استخدام Round-Robin للعملاء الذين لديهم نفس الأولوية.", "ApplyTagsHelpTextAdd": "إضافة: أضف العلامات إلى قائمة العلامات الموجودة", "ApplyTagsHelpTextHowToApplyApplications": "كيفية تطبيق العلامات على الأفلام المختارة", @@ -341,7 +341,7 @@ "More": "أكثر", "Track": "أثر", "Year": "عام", - "ConnectionLostReconnect": "سيحاول Radarr الاتصال تلقائيًا ، أو يمكنك النقر فوق إعادة التحميل أدناه.", + "ConnectionLostReconnect": "سيحاول {appName} الاتصال تلقائيًا ، أو يمكنك النقر فوق إعادة التحميل أدناه.", "DeleteAppProfileMessageText": "هل أنت متأكد من أنك تريد حذف ملف تعريف الجودة {0}", "RecentChanges": "التغييرات الأخيرة", "WhatsNew": "ما هو الجديد؟", @@ -355,5 +355,25 @@ "ResetAPIKeyMessageText": "هل أنت متأكد أنك تريد إعادة تعيين مفتاح API الخاص بك؟", "RestartProwlarr": "أعد تشغيل {appName}", "CustomFilter": "مرشحات مخصصة", - "IndexerHDBitsSettingsMediums": "متوسط" + "IndexerHDBitsSettingsMediums": "متوسط", + "GrabRelease": "انتزاع الإصدار", + "ProxyValidationBadRequest": "فشل اختبار الوكيل. رمز الحالة: {statusCode}", + "Script": "النصي", + "BuiltIn": "مدمج", + "PublishedDate": "تاريخ النشر", + "AllSearchResultsHiddenByFilter": "يتم إخفاء جميع النتائج بواسطة عامل التصفية المطبق", + "NoEventsFound": "لم يتم العثور على أحداث", + "RestartReloadNote": "ملاحظة: سيتم إعادة تشغيل {appName} تلقائيًا وإعادة تحميل واجهة المستخدم أثناء عملية الاستعادة.", + "UpdateAppDirectlyLoadError": "تعذر تحديث {appName} مباشرة ،", + "DockerUpdater": "تحديث حاوية عامل الإرساء لتلقي التحديث", + "Download": "تحميل", + "ErrorRestoringBackup": "خطأ في استعادة النسخة الاحتياطية", + "ExternalUpdater": "تم تكوين {appName} لاستخدام آلية تحديث خارجية", + "InstallLatest": "تثبيت الأحدث", + "AptUpdater": "استخدم apt لتثبيت التحديث", + "Clone": "قريب", + "Stats": "الحالة", + "CurrentlyInstalled": "مثبتة حاليا", + "Season": "السبب", + "Mixed": "ثابت" } diff --git a/src/NzbDrone.Core/Localization/Core/bg.json b/src/NzbDrone.Core/Localization/Core/bg.json index 6a8690664..913275a68 100644 --- a/src/NzbDrone.Core/Localization/Core/bg.json +++ b/src/NzbDrone.Core/Localization/Core/bg.json @@ -90,7 +90,7 @@ "SSLPort": "SSL порт", "Status": "Състояние", "System": "Система", - "SystemTimeCheckMessage": "Системното време е изключено с повече от 1 ден. Планираните задачи може да не се изпълняват правилно, докато времето не бъде коригирано", + "SystemTimeHealthCheckMessage": "Системното време е изключено с повече от 1 ден. Планираните задачи може да не се изпълняват правилно, докато времето не бъде коригирано", "TestAll": "Тествайте всички", "Title": "Заглавие", "Today": "Днес", @@ -120,7 +120,7 @@ "Grabbed": "Грабната", "Grabs": "Грабнете", "Health": "Здраве", - "HealthNoIssues": "Няма проблеми с вашата конфигурация", + "NoIssuesWithYourConfiguration": "Няма проблеми с вашата конфигурация", "HomePage": "Начална страница", "Hostname": "Име на хост", "IgnoredAddresses": "Игнорирани адреси", @@ -153,7 +153,7 @@ "UISettings": "Настройки на потребителския интерфейс", "UnableToAddANewApplicationPleaseTryAgain": "Не може да се добави ново известие, моля, опитайте отново.", "UnableToAddANewAppProfilePleaseTryAgain": "Не може да се добави нов качествен профил, моля, опитайте отново.", - "UnableToLoadBackups": "Архивите не могат да се заредят", + "BackupsLoadError": "Архивите не могат да се заредят", "AllIndexersHiddenDueToFilter": "Всички филми са скрити поради приложен филтър.", "Level": "Ниво", "ApplicationStatusCheckAllClientMessage": "Всички списъци са недостъпни поради неуспехи", @@ -329,7 +329,7 @@ "Queued": "На опашка", "Remove": "Премахване", "Replace": "Сменете", - "TheLatestVersionIsAlreadyInstalled": "Вече е инсталирана най-новата версия на {0}", + "OnLatestVersion": "Вече е инсталирана най-новата версия на {0}", "Genre": "Жанрове", "ApplyTagsHelpTextRemove": "Премахване: Премахнете въведените тагове", "ApplyTagsHelpTextHowToApplyIndexers": "Как да приложите тагове към избраните филми", @@ -355,5 +355,67 @@ "ResetAPIKeyMessageText": "Наистина ли искате да нулирате своя API ключ?", "RestartProwlarr": "Рестартирайте {appName}", "IndexerHDBitsSettingsMediums": "Среден", - "CustomFilter": "Персонализирани филтри" + "CustomFilter": "Персонализирани филтри", + "GrabRelease": "Grab Release", + "ProxyValidationBadRequest": "Неуспешно тестване на прокси. Код на състоянието: {statusCode}", + "BuiltIn": "Вграден", + "Script": "Сценарий", + "PublishedDate": "Дата на публикуване", + "AllSearchResultsHiddenByFilter": "Всички резултати са скрити от приложения филтър", + "DockerUpdater": "актуализирайте контейнера на докера, за да получите актуализацията", + "Download": "Изтегли", + "ErrorRestoringBackup": "Грешка при възстановяване на архивиране", + "ExternalUpdater": "{appName} е конфигуриран да използва външен механизъм за актуализация", + "NoEventsFound": "Няма намерени събития", + "RestartReloadNote": "Забележка: {appName} автоматично ще рестартира и презареди потребителския интерфейс по време на процеса на възстановяване.", + "UpdateAppDirectlyLoadError": "Не може да се актуализира {appName} директно,", + "AptUpdater": "Използвайте apt, за да инсталирате актуализацията", + "InstallLatest": "Инсталирайте най-новите", + "Clone": "Близо", + "ActiveApps": "Активни приложения", + "ActiveIndexers": "Активни индиксатори", + "AddApplication": "добави приложение", + "Season": "Причина", + "CurrentlyInstalled": "Понастоящем инсталиран", + "DownloadClientSettingsAddPaused": "Добави на пауза", + "Encoding": "Кодиране", + "Episode": "епизод", + "Applications": "Приложения", + "Publisher": "Издател", + "Id": "ИН", + "Theme": "Тема", + "Label": "Етикет", + "Categories": "Категории", + "Album": "албум", + "Artist": "изпълнител", + "AddConnection": "Добави връзка", + "AddConnectionImplementation": "Добави връзка - {implementationName}", + "AddDownloadClientImplementation": "Добави клиент за изтегляне - {implementationName}", + "AuthenticationRequiredPasswordConfirmationHelpTextWarning": "Потвърдете новата парола", + "Default": "Подразбиране", + "Any": "Всеки", + "ApplicationUrlHelpText": "Външният URL адрес на това приложение, включително http(s)://, порт и основно URL", + "Database": "База данни", + "Destination": "Дестинация", + "DownloadClientAriaSettingsDirectoryHelpText": "Незадължително локация за изтеглянията, оставете празно, за да използвате локацията по подразбиране на Aria2", + "DownloadClientDelugeSettingsUrlBaseHelpText": "Добавя префикс към url адреса на deluge json, вижте {url}", + "Directory": "Директория", + "AddIndexerImplementation": "Добави индексатор - {implementationName}", + "AuthenticationRequiredHelpText": "Променете за кои заявки се изисква удостоверяване. Не променяйте, освен ако не разбирате рисковете.", + "AuthenticationRequiredPasswordHelpTextWarning": "Въведете нова парола", + "DownloadClientDownloadStationSettingsDirectoryHelpText": "Незадължителна споделена папка, в която да се поставят изтеглянията, оставете празно, за да използвате местоположението по подразбиране на Download Station", + "DownloadClientFloodSettingsAdditionalTags": "Допълнителни тагове", + "DownloadClientFloodSettingsAdditionalTagsHelpText": "Добавя свойствата на медията като тагове. Напътствията са примери.", + "DownloadClientFloodSettingsTagsHelpText": "Първоначални тагове на изтегляне. За да бъде разпознато едно изтегляне, то трябва да има всички начални тагове. По този начин се избягват конфликти с необвързани с приложение изтегляния.", + "ApplicationURL": "URL адрес на приложението", + "AuthenticationRequired": "Изисква се удостоверяване", + "ApplyChanges": "Прилагане на промените", + "ApiKeyValidationHealthCheckMessage": "Моля, актуализирайте API ключа си така, че да съдържа поне {length} знака. Можете да направите това чрез настройките или конфигурационния файл", + "AppUpdated": "{appName} Актуализиран", + "AppUpdatedVersion": "{appName} е актуализиранa до версия `{version}`, за да получите най-новите промени, ще трябва да презаредите {appName}", + "Donate": "Дарете", + "AddCustomFilter": "Добави персонализиран филтър", + "AuthenticationMethod": "Метод за удостоверяване", + "AuthenticationMethodHelpTextWarning": "Моля, изберете валиден метод за удостоверяване", + "BlackholeFolderHelpText": "Папка, в която {appName} ще съхранява файла {extension}" } diff --git a/src/NzbDrone.Core/Localization/Core/ca.json b/src/NzbDrone.Core/Localization/Core/ca.json index 77f343811..83a720b51 100644 --- a/src/NzbDrone.Core/Localization/Core/ca.json +++ b/src/NzbDrone.Core/Localization/Core/ca.json @@ -43,7 +43,7 @@ "Type": "Tipus", "UILanguageHelpTextWarning": "Es requereix una recàrrega del navegador", "UISettings": "Configuració de la interfície", - "UnableToLoadBackups": "No es poden carregar còpies de seguretat", + "BackupsLoadError": "No es poden carregar còpies de seguretat", "DownloadClientsLoadError": "No es poden carregar els clients de baixada", "UnableToLoadTags": "No es poden carregar les etiquetes", "UnableToLoadUISettings": "No es pot carregar la configuració de la IU", @@ -136,7 +136,7 @@ "Priority": "Prioritat", "SendAnonymousUsageData": "Envia dades d'ús anònimes", "SetTags": "Estableix etiquetes", - "SystemTimeCheckMessage": "L'hora del sistema està apagada durant més d'1 dia. És possible que les tasques programades no s'executin correctament fins que no es corregeixi l'hora", + "SystemTimeHealthCheckMessage": "L'hora del sistema està apagada durant més d'1 dia. És possible que les tasques programades no s'executin correctament fins que no es corregeixi l'hora", "TableOptions": "Opcions de taula", "TableOptionsColumnsMessage": "Trieu quines columnes són visibles i en quin ordre apareixen", "Columns": "Columnes", @@ -216,7 +216,7 @@ "SettingsShowRelativeDatesHelpText": "Mostra dates relatives (avui/ahir/etc) o absolutes", "ShowSearch": "Mostra la cerca", "ShowSearchHelpText": "Mostra el botó de cerca al passar el cursor", - "Shutdown": "Tanca", + "Shutdown": "Apaga", "Sort": "Ordena", "Source": "Font", "SSLCertPassword": "Contrasenya de certificat SSL", @@ -267,7 +267,7 @@ "Docker": "Docker", "Donations": "Donacions", "DownloadClientStatusSingleClientHealthCheckMessage": "Baixa els clients no disponibles a causa d'errors: {downloadClientNames}", - "HealthNoIssues": "No hi ha cap problema amb la configuració", + "NoIssuesWithYourConfiguration": "No hi ha cap problema amb la configuració", "HideAdvanced": "Amaga avançat", "History": "Història", "HomePage": "Pàgina d'inici", @@ -340,7 +340,7 @@ "UILanguageHelpText": "Idioma que utilitzarà {appName} per a la interfície d'usuari", "Remove": "Elimina", "Replace": "Substitueix", - "TheLatestVersionIsAlreadyInstalled": "La darrera versió de {appName} ja està instal·lada", + "OnLatestVersion": "La darrera versió de {appName} ja està instal·lada", "ThemeHelpText": "Canvieu el tema de la interfície d'usuari de l'aplicació, el tema \"Automàtic\" utilitzarà el tema del vostre sistema operatiu per configurar el mode clar o fosc. Inspirat en {inspiredBy}.", "ApplicationURL": "URL de l'aplicació", "Publisher": "Editor", @@ -420,7 +420,7 @@ "days": "dies", "Album": "Àlbum", "Artist": "Artista", - "AddApplicationImplementation": "Afegeix una condició - {implementationName}", + "AddApplicationImplementation": "Afegeix una aplicació - {implementationName}", "AddIndexerProxyImplementation": "Afegeix un indexador - {implementationName}", "Category": "Categoria", "Clone": "Clona", @@ -443,5 +443,302 @@ "DownloadClientSettingsUrlBaseHelpText": "Afegeix un prefix a l'URL {clientName}, com ara {url}", "DownloadClientTransmissionSettingsDirectoryHelpText": "Ubicació opcional per a les baixades, deixeu-lo en blanc per utilitzar la ubicació predeterminada de Transmission", "DownloadClientRTorrentSettingsDirectoryHelpText": "Ubicació opcional de les baixades completades, deixeu-lo en blanc per utilitzar la ubicació predeterminada de rTorrent", - "IndexerHDBitsSettingsMediums": "Mitjans" + "IndexerHDBitsSettingsMediums": "Mitjans", + "Query": "Consulta", + "AddApplication": "Afegeix una aplicació", + "AddRemoveOnly": "Només afegeix i elimina", + "AddSyncProfile": "Afegeix perfil de sincronització", + "Auth": "Autenticació", + "ActiveIndexers": "Indexadors actius", + "Description": "Descripció", + "DeleteSelectedIndexers": "Suprimeix els indexadors seleccionats", + "IndexerSettingsBaseUrlHelpText": "Seleccioneu quina URL base utilitzarà {appName} per a les sol·licituds al lloc", + "ActiveApps": "Aplicacions actives", + "AddCategory": "Afegeix una categoria", + "AddDownloadClientToProwlarr": "Afegir un client de baixada permet que {appName} enviï publicacions directament des de la interfície d'usuari mentre fa una cerca manual.", + "AddIndexerProxy": "Afegeix un servidor intermediari per a l'indexador", + "AddNewIndexer": "Afegeix un nou indexador", + "AddToDownloadClient": "Afegeix llançament al client de baixades", + "AddedToDownloadClient": "Baixada afegida al client", + "DevelopmentSettings": "Configuració de desenvolupament", + "Mixed": "Combinat", + "Apps": "Aplicacions", + "Privacy": "Privacitat", + "Redirect": "Redirecció", + "Stats": "Estadístiques", + "Private": "Privat", + "Proxies": "Servidors intermediaris", + "Public": "Públic", + "DeleteSelectedIndexer": "Suprimeix els indexadors seleccionats", + "EditSyncProfile": "Afegeix perfil de sincronització", + "Menu": "Menú", + "OnGrabHelpText": "Al capturar llançament", + "ProxyValidationBadRequest": "No s'ha pogut provar el servidor intermediari. Codi d'estat: {statusCode}", + "Default": "Per defecte", + "GrabRelease": "Captura novetat", + "ManualGrab": "Captura manual", + "PrioritySettings": "Prioritat: {priority}", + "Any": "Qualsevol", + "BuiltIn": "Integrat", + "Script": "Script", + "InfoUrl": "URL d'informació", + "PublishedDate": "Data de publicació", + "Redirected": "Redirecció", + "AllSearchResultsHiddenByFilter": "Tots els resultats estan ocults pel filtre aplicat.", + "HealthMessagesInfoBox": "Podeu trobar més informació sobre la causa d'aquests missatges de comprovació de salut fent clic a l'enllaç wiki (icona del llibre) al final de la fila o consultant els vostres [registres]({link}). Si teniu problemes per a interpretar aquests missatges, podeu posar-vos en contacte amb el nostre suport als enllaços següents.", + "AptUpdater": "Utilitzeu apt per a instal·lar l'actualització", + "DockerUpdater": "actualitzeu el contenidor Docker per a rebre l'actualització", + "Download": "Baixa", + "ErrorRestoringBackup": "S'ha produït un error en restaurar la còpia de seguretat", + "ExternalUpdater": "{appName} està configurat per a utilitzar un mecanisme d'actualització extern", + "FailedToFetchUpdates": "No s'han pogut obtenir les actualitzacions", + "LogFilesLocation": "Els fitxers de registre es troben a: {location}", + "Logout": "Tanca la sessió", + "NoEventsFound": "No s'han trobat esdeveniments", + "RestartReloadNote": "Nota: {appName} es reiniciarà i tornarà a carregar automàticament la interfície d'usuari durant el procés de restauració.", + "TheLogLevelDefault": "El nivell de registre per defecte és \"Info\" i es pot canviar a [Configuració general](/configuració/general)", + "UpdateAppDirectlyLoadError": "No es pot actualitzar {appName} directament,", + "WouldYouLikeToRestoreBackup": "Voleu restaurar la còpia de seguretat '{name}'?", + "InstallLatest": "Instal·la l'últim", + "CurrentlyInstalled": "Instal·lat actualment", + "DownloadClientSettingsAddPaused": "Afegeix pausats", + "Install": "Instal·la", + "DownloadClientFloodSettingsAdditionalTags": "Etiquetes addicionals", + "DownloadClientFreeboxSettingsApiUrl": "URL de l'API", + "DownloadClientFreeboxSettingsAppId": "Identificador de l'aplicació", + "PreviouslyInstalled": "Instal·lat anteriorment", + "PasswordConfirmation": "Confirmeu la contrasenya", + "IndexerHDBitsSettingsOriginsHelpText": "Si no s'especifica, s'utilitzen totes les opcions.", + "MinimumSeeders": "Seeders mínims", + "SeedRatio": "Ràtio de la llavor", + "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "Si un torrent està bloquejat per un hash, pot ser que no es rebutgi correctament durant el RSS/Search per a alguns indexadors, habilitant això permetrà que es rebutgi després que s'agafi el torrent, però abans que s'enviï al client.", + "SeedTime": "Temps de la llavor", + "DefaultCategory": "Categoria predeterminada", + "IndexerVipExpiredHealthCheckMessage": "Els beneficis VIP de l'indexador han caducat: {indexerNames}", + "MassEditor": "Editor de masses", + "NoSearchResultsFound": "No s'han trobat resultats de cerca, proveu de realitzar una nova cerca a continuació.", + "NotSupported": "No suportat", + "OverrideAndAddToDownloadClient": "Sobreescriu i afegeix al client de baixada", + "PackSeedTime": "Temps de la llavor del paquet", + "SearchAllIndexers": "Cerca tots els indexadors", + "SearchIndexers": "Cerca indexadors", + "SeedTimeHelpText": "L'hora en què un torrent s'ha de sembrar abans d'aturar-se, buit és el predeterminat de l'aplicació", + "SettingsIndexerLogging": "Registre de l'indexador millorat", + "TotalIndexerSuccessfulGrabs": "Indexador total correcte", + "BookSearch": "Cerca de llibres", + "ClearHistoryMessageText": "Esteu segur que voleu netejar tot l'historial de {appName}?", + "IndexerGazelleGamesSettingsFreeleechOnlyHelpText": "Cerca només els llançaments de freeleech", + "ClearHistory": "Neteja l'historial", + "IndexerTorrentSyndikatSettingsApiKeyHelpText": "Clau de l'API del lloc", + "IndexerGazelleGamesSettingsApiKeyHelpText": "Clau API del lloc (trobada a Configuració => Accés)", + "IndexerBeyondHDSettingsRssKeyHelpText": "Clau RSS del lloc (trobada a Seguretat => Clau RSS)", + "ApplicationsLoadError": "No s'ha pogut carregar la llista d'aplicacions", + "BookSearchTypes": "Tipus de cerca de llibres", + "DownloadClientCategory": "Baixa la categoria del client", + "IndexerAuth": "Autor de l'indexador", + "IndexerBeyondHDSettingsLimitedOnly": "Només limitat", + "IndexerBeyondHDSettingsRefundOnly": "Només el reemborsament", + "IndexerBeyondHDSettingsRefundOnlyHelpText": "Cerca només el reemborsament", + "IndexerBeyondHDSettingsRewindOnlyHelpText": "Cerca només el rebobinat", + "IndexerBeyondHDSettingsSearchTypes": "Tipus de cerca", + "IndexerBeyondHDSettingsSearchTypesHelpText": "Seleccioneu els tipus de llançaments que us interessin. Si no hi ha cap seleccionat, s'utilitzen totes les opcions.", + "IndexerDownloadClientHelpText": "Especifiqueu quin client de baixada s'utilitza per a les captures fetes a {appName} des d'aquest indexador", + "IndexerGazelleGamesSettingsApiKeyHelpTextWarning": "Ha de tenir permisos d'usuari i torrents", + "IndexerGazelleGamesSettingsSearchGroupNames": "Cerca noms de grup", + "IndexerHDBitsSettingsFreeleechOnlyHelpText": "Mostra només els llançaments de freeleech", + "IndexerHDBitsSettingsUseFilenames": "Utilitza els noms de fitxer", + "IndexerHDBitsSettingsUseFilenamesHelpText": "Marqueu aquesta opció si voleu utilitzar noms de fitxer torrent com a títols de llançament", + "IndexerHDBitsSettingsUsernameHelpText": "Nom d'usuari del lloc", + "IndexerHealthCheckNoIndexers": "No hi ha indexadors activats, {appName} no retornarà els resultats de la cerca", + "IndexerHistoryLoadError": "Error en carregar l'historial de l'indexador", + "IndexerIPTorrentsSettingsCookieUserAgent": "Agent d'usuari de la galeta", + "IndexerIPTorrentsSettingsFreeleechOnlyHelpText": "Cerca només els llançaments de freeleech", + "IndexerInfo": "Informació de l'indexador", + "IndexerNebulanceSettingsApiKeyHelpText": "Clau API de la Configuració de l'usuari . Claus Api. La clau ha de tenir permisos de llista i baixada", + "IndexerNewznabSettingsVipExpirationHelpText": "Data d'entrada (yyyy-mm-dd) per a la caducitat VIP o en blanc, {appName} notificarà 1 setmana des de la caducitat de VIP", + "IndexerNoDefinitionCheckHealthCheckMessage": "Els indexadors no tenen definició i no funcionaran: {indexerNames}. Suprimiu i (o torneu a afegir) a {appName}.", + "IndexerObsoleteCheckMessage": "Els indexadors estan obsolets o s'han actualitzat: {0}. Suprimiu i (o torneu a afegir) a {appName}", + "IndexerPassThePopcornSettingsApiKeyHelpText": "Clau de l'API del lloc", + "IndexerRss": "Indexador RSS", + "IndexerSettingsGrabLimitHelpText": "El nombre màxim de captures especificat per la unitat respectiva que {appName} permetrà al lloc", + "IndexerSettingsPackSeedTimeIndexerHelpText": "L'hora en què un paquet (temporada o discografia) s'ha de sembrar el torrent abans d'aturar-se, buit és el valor predeterminat de l'aplicació", + "IndexerSettingsPreferMagnetUrl": "Prefereix l'URL de l'imant", + "IndexerSettingsPreferMagnetUrlHelpText": "Quan està activat, aquest indexador preferirà l'ús d'URLs magnet per a les captures amb enllaços de reserva a torrents", + "IndexerSettingsQueryLimitHelpText": "El nombre màxim de consultes especificat per la unitat respectiva que {appName} permetrà al lloc", + "InitialFailure": "Fallada inicial", + "NoIndexerCategories": "No s'ha trobat cap categoria per a aquest indexador", + "RepeatSearch": "Cerca repetida", + "SettingsLogRotateHelpText": "Nombre màxim de fitxers de registre a mantenir desats a la carpeta de registres", + "SyncProfile": "Perfil de sincronització", + "TotalUserAgentGrabs": "Total d'agents d'usuari", + "UnableToLoadAppProfiles": "No s'han pogut carregar els perfils de l'aplicació", + "VipExpiration": "Caducitat VIP", + "IndexerOrpheusSettingsApiKeyHelpText": "Clau API del lloc (trobada a Configuració => Accés)", + "IndexerRedactedSettingsApiKeyHelpText": "Clau API del lloc (trobada a Configuració => Accés)", + "ProwlarrSupportsAnyIndexer": "{appName} admet molts indexadors, a més de qualsevol indexador que utilitzi l'estàndard Newznab/Torznab utilitzant 'Generic Newznab' (per usenet) o 'Generic Torznab' (per torrents). Cerca i selecciona el teu indexador des de sota.", + "DownloadClientSettingsDefaultCategorySubFolderHelpText": "Categoria alternativa predeterminada si no hi ha cap categoria assignada per a un llançament. Afegir una categoria específica a {appName} evita conflictes amb baixades no relacionades amb {appName}. L'ús d'una categoria és opcional, però molt recomanable.", + "AppProfileSelectHelpText": "Els perfils d'aplicació s'utilitzen per controlar RSS, la cerca automàtica i la configuració de cerca interactiva en sincronitzar aplicacions", + "AudioSearch": "Cerca d'àudio", + "IndexerSettingsAppsMinimumSeeders": "Aplicacions de cercadors mínims", + "DeleteApplication": "Suprimeix l'aplicació", + "DownloadClientSettingsPriorityItemHelpText": "Prioritat a usar en capturar elements", + "DownloadClientSettingsDefaultCategoryHelpText": "Categoria alternativa predeterminada si no hi ha cap categoria assignada per a un llançament. Afegir una categoria específica a {appName} evita conflictes amb baixades no relacionades amb {appName}. L'ús d'una categoria és opcional, però molt recomanable.", + "EditCategory": "Edita la categoria", + "IndexerQuery": "Consulta de l'indexador", + "IndexerSettingsBaseUrl": "Url base", + "IndexerSettingsCookieHelpText": "Cookie del lloc", + "IndexerSettingsLimitsUnitHelpText": "La unitat de temps per comptar els límits per indexador", + "IndexerSettingsPackSeedTime": "Temps de la llavor del paquet", + "IndexerSite": "Lloc indexador", + "IndexerTagsHelpTextWarning": "Les etiquetes s'han d'utilitzar amb precaució, poden tenir efectes no desitjats. Un indexador amb una etiqueta només sincronitzarà amb aplicacions amb la mateixa etiqueta.", + "IndexerSettingsSummary": "Configura diversos paràmetres globals de l'indexador.", + "ManageApplications": "Gestiona les aplicacions", + "PackSeedTimeHelpText": "L'hora en què un paquet (temporada o discografia) s'ha de sembrar el torrent abans d'aturar-se, buit és el valor predeterminat de l'aplicació", + "PreferMagnetUrl": "Prefereix l'URL de l'imant", + "PreferMagnetUrlHelpText": "Quan està activat, aquest indexador preferirà l'ús d'URLs magnet per a les captures amb enllaços de reserva a torrents", + "ProwlarrDownloadClientsInAppOnlyAlert": "Els clients de baixada només són per a les cerques a l'aplicació {appName} i no sincronitzen amb les aplicacions. No hi ha plans per afegir aquesta funcionalitat.", + "IndexerBeyondHDSettingsRewindOnly": "Només rebobina", + "ProxyValidationUnableToConnect": "No s'ha pogut connectar al servidor intermediari: {exceptionMessage}. Comprova els detalls del registre que envolta aquest error", + "QueryOptions": "Opcions de la consulta", + "TestAllApps": "Prova totes les aplicacions", + "TotalHostQueries": "Total de consultes de l'amfitrió", + "AverageGrabs": "Mitjana d'herba", + "AverageQueries": "Mitjana de consultes", + "FilterPlaceHolder": "Cerca indexadors", + "IndexerBeyondHDSettingsLimitedOnlyHelpText": "Cerca només freeleech (Limited UL)", + "IndexerBeyondHDSettingsApiKeyHelpText": "Clau API del lloc (trobada a Seguretat => Clau API)", + "IndexerSettingsVipExpiration": "Caducitat VIP", + "IndexerVipExpiringHealthCheckMessage": "Els beneficis VIP de l'indexador expiraran aviat: {indexerNames}", + "LastFailure": "Darrera fallada", + "MovieSearch": "Cerca de pel·lícules", + "MovieSearchTypes": "Tipus de cerca de pel·lícules", + "MusicSearchTypes": "Tipus de cerca de música", + "QueryType": "Tipus de consulta", + "RssQueries": "Consultes RSS", + "SyncLevelFull": "Sincronització completa: mantindrà els indexadors d'aquesta aplicació completament sincronitzats. Els canvis fets als indexadors a {appName} se sincronitzen amb aquesta aplicació. Qualsevol canvi fet a indexadors remotament dins d'aquesta aplicació serà anul·lat per {appName} en la següent sincronització.", + "TotalHostGrabs": "Total d'amfitrions", + "TotalQueries": "Total de consultes", + "NoApplicationsFound": "No s'ha trobat cap aplicació", + "SyncProfiles": "Sincronitza els perfils", + "TorznabUrl": "Url Torznab", + "TvSearch": "Cerca de TV", + "DeleteIndexerProxy": "Suprimeix el servidor intermediari de l'indexador", + "DisabledUntil": "Desactivat fins", + "GrabTitle": "Captura el títol", + "SettingsIndexerLoggingHelpText": "Registra dades addicionals de l'indexador", + "SyncLevel": "Nivell de sincronització", + "AdvancedSettingsHiddenClickToShow": "Configuració avançada oculta, feu clic per mostrar", + "AdvancedSettingsShownClickToHide": "Configuració avançada mostrada, feu clic per amagar", + "AppsMinimumSeeders": "Aplicacions de cercadors mínims", + "AppsMinimumSeedersHelpText": "«Mínims filtradors requerits per les Aplicacions perquè l'indexador s'agafi", + "CountIndexersAvailable": "{count} indexador(s) disponible", + "HistoryCleanup": "Neteja de l'historial", + "HistoryDetails": "Detalls de l'historial", + "SettingsFilterSentryEventsHelpText": "Filtra els esdeveniments d'error d'usuari coneguts perquè s'enviïn com a Analytics", + "MappedCategories": "Categories assignades", + "AppSettingsSummary": "Aplicacions i paràmetres per configurar com {appName} interactua amb els vostres programes PVR", + "ConnectSettingsSummary": "Notificacions i scripts personalitzats", + "DeleteClientCategory": "Suprimeix la categoria del client de baixada", + "FullSync": "Sincronització completa", + "IndexerAlreadySetup": "Almenys una instància de l'indexador ja està configurada", + "RawSearchSupported": "S'admet la cerca RAW", + "RssFeed": "Canal RSS", + "SearchTypes": "Tipus de cerca", + "SeedRatioHelpText": "La relació a la qual ha d'arribar un torrent abans d'aturar-se, buida és la predeterminada de l'aplicació", + "SemiPrivate": "Semi-Privada", + "TotalIndexerQueries": "Total de consultes de l'indexador", + "TotalUserAgentQueries": "Total de consultes d'agents d'usuari", + "GoToApplication": "Ves a l'aplicació", + "Url": "Url", + "AreYouSureYouWantToDeleteIndexer": "Esteu segur que voleu suprimir '{name}' de {appName}?", + "AverageResponseTimesMs": "Temps mitjà de resposta de l'indexador (ms)", + "FoundCountReleases": "S'han trobat {itemCount} versions", + "AuthQueries": "Consultes d'Autorització", + "BasicSearch": "Cerca bàsica", + "IndexerName": "Nom de l'indexador", + "IndexerStatus": "Estat de l'indexador", + "IndexerTagsHelpText": "Utilitzeu etiquetes per especificar els intermediaris de l'indexador o a quines aplicacions se sincronitza l'indexador.", + "NewznabUrl": "Url Newznab", + "SearchCountIndexers": "Cerca {count} indexador", + "UnableToLoadDevelopmentSettings": "No s'han pogut carregar els paràmetres de desenvolupament", + "SettingsFilterSentryEvents": "Filtra els esdeveniments d'anàlisi", + "ApplicationTagsHelpText": "Sincronitza els indexadors d'aquesta aplicació que tenen una o més etiquetes coincidents. Si no es llisten etiquetes aquí, llavors no s'impedirà la sincronització d'indexadors a causa de les seves etiquetes.", + "ApplicationTagsHelpTextWarning": "Les etiquetes s'han d'utilitzar amb precaució, poden tenir efectes no desitjats. Una aplicació amb una etiqueta només sincronitzarà amb els indexadors que tinguin la mateixa etiqueta.", + "DeleteSelectedApplications": "Suprimeix les aplicacions seleccionades", + "DownloadClientsSettingsSummary": "Baixa la configuració dels clients per a la integració a la cerca de la interfície d'usuari {appName}", + "ElapsedTime": "Temps transcorregut", + "EnableIndexer": "Habilita l'indexador", + "EnableRssHelpText": "Habilita el canal RSS per a l'indexador", + "IncludeManualGrabsHelpText": "Inclou les notes manuals fetes a {appName}", + "IndexerFailureRate": "Taxa de fallada de l'indexador", + "ProwlarrSupportsAnyDownloadClient": "{appName} admet qualsevol dels clients de baixada que es llisten a continuació.", + "TotalGrabs": "Grabs totals", + "IndexerDetails": "Detalls de l'indexador", + "IndexerPriorityHelpText": "Prioritat de l'indexador des de l'1 (el més alt) fins al 50 (el més oest). Per defecte: 25.", + "IndexerProxy": "Servidor intermediari de l'indexador", + "UISettingsSummary": "Opcions de data, idioma i color defectuoses", + "IndexerCategories": "Categories de l'indexador", + "IndexerPassThePopcornSettingsFreeleechOnlyHelpText": "Cerca només els llançaments de freeleech", + "SearchCapabilities": "Capacitats de cerca", + "SearchType": "Tipus de cerca", + "SettingsLogSql": "Registre Sql", + "SyncLevelAddRemove": "Afegeix i elimina només: quan s'afegeixen o s'eliminen els indexadors de {appName}, s'actualitzarà aquesta aplicació remota.", + "IndexerProxies": "Propis de l'indexador", + "ApplicationSettingsSyncRejectBlocklistedTorrentHashes": "Sincronitza les fulles del torrent de la llista de blocs en bloc mentre s'agafa", + "CertificateValidationHelpText": "Canvia l'estricta validació de la certificació HTTPS", + "IndexerDisabled": "Indexador desactivat", + "IndexerFileListSettingsPasskeyHelpText": "Contrasenya del lloc (Aquesta és la cadena alfanumèrica a l'URL del seguidor que es mostra al client de baixada)", + "IndexerSettingsQueryLimit": "Límit de consulta", + "IndexerHDBitsSettingsPasskeyHelpText": "Contrasenya dels detalls de l'usuari", + "IndexerSettingsPasskey": "Clau de pas", + "ClickToChangeQueryOptions": "Feu clic per a canviar les opcions de consulta", + "EnabledRedirected": "Activat, redirigit", + "Parameters": "Paràmetres", + "QueryResults": "Resultats de la consulta", + "RedirectHelpText": "Redirigeix la sol·licitud de baixada entrant per a l'indexador i passa la captura directament en lloc de intermediaris a través de {appName}", + "UnableToLoadIndexerProxies": "No s'han pogut carregar els intermediaris de l'indexador", + "IndexerId": "ID de l'indexador", + "IndexerAlphaRatioSettingsExcludeScene": "Exclou l'escena", + "IndexerAlphaRatioSettingsExcludeSceneHelpText": "Exclou els llançaments d'escenes dels resultats", + "IndexerAlphaRatioSettingsFreeleechOnlyHelpText": "Cerca només els llançaments de freeleech", + "IndexerBeyondHDSettingsFreeleechOnlyHelpText": "Cerca només els llançaments de freeleech", + "IndexerFileListSettingsFreeleechOnlyHelpText": "Cerca només els llançaments de freeleech", + "IndexerFileListSettingsUsernameHelpText": "Nom d'usuari del lloc", + "IndexerGazelleGamesSettingsSearchGroupNamesHelpText": "Cerca publicacions per noms de grup", + "IndexerHDBitsSettingsOrigins": "Orígens", + "IndexerIPTorrentsSettingsCookieUserAgentHelpText": "Agent d'usuari associat a la cookie utilitzada des del navegador", + "IndexerNewznabSettingsApiKeyHelpText": "Clau de l'API del lloc", + "IndexerNzbIndexSettingsApiKeyHelpText": "Clau de l'API del lloc", + "IndexerSettingsAppsMinimumSeedersHelpText": "«Mínims filtradors requerits per les Aplicacions perquè l'indexador s'agafi", + "IndexerSettingsFreeleechOnly": "Només Freeleech", + "IndexerSettingsGrabLimit": "Límit de captura", + "IndexerSettingsLimitsUnit": "Unitats de límits", + "IndexerSettingsRssKey": "Clau RSS", + "SelectIndexers": "Selecciona els indexadors", + "SettingsSqlLoggingHelpText": "Registra totes les consultes SQL de {appName}", + "SyncAppIndexers": "Sincronitza els indexadors d'aplicacions", + "AppProfileInUse": "Perfil d'aplicació en ús", + "DeleteAppProfile": "Suprimeix el perfil de l'aplicació", + "TVSearchTypes": "Tipus de cerca de TV", + "IndexerMTeamTpSettingsFreeleechOnlyHelpText": "Cerca només els llançaments de freeleech", + "IndexerMTeamTpSettingsApiKeyHelpText": "Clau API del Lloc (trobada a Tauler de control de l'usuari => Seguretat => Laboratori)", + "MinimumSeedersHelpText": "Visors mínims requerits per l'aplicació perquè l'indexador s'agafi", + "NoIndexerHistory": "No s'ha trobat cap historial per a aquest indexador", + "SearchQueries": "Cerca consultes", + "SettingsConsoleLogLevel": "Nivell de registre de la consola", + "IndexerPassThePopcornSettingsGoldenPopcornOnly": "Només blat de moro daurat", + "IndexerPassThePopcornSettingsGoldenPopcornOnlyHelpText": "Cerca només els llançaments Golden Popcorn", + "Open": "Obre", + "ProwlarrDownloadClientsAlert": "Si voleu fer cerques directament dins de {appName}, heu d'afegir Clients de Baixades. En cas contrari, no cal afegir-les aquí. Per a les cerques des de les teves Apps, els clients de descàrrega configurats s'utilitzen en el seu lloc.", + "Website": "Lloc web", + "DownloadClientQbittorrentSettingsUseSslHelpText": "Utilitza una connexió segura. Vegeu Opcions -> Interfície web -> 'Utilitza HTTPS en comptes d'HTTP' a qBittorrent.", + "IndexerAvistazSettingsFreeleechOnlyHelpText": "Cerca només els llançaments de freeleech", + "IndexerAvistazSettingsPasswordHelpText": "Contrasenya del lloc", + "IndexerAvistazSettingsPidHelpText": "PID de la pàgina del meu compte o del meu perfil", + "IndexerAvistazSettingsUsernameHelpText": "Nom d'usuari del lloc", + "IndexerAvistazSettingsUsernameHelpTextWarning": "Només el rang de membre i superior pot utilitzar l'API en aquest indexador.", + "SelectedCountOfCountReleases": "S'han seleccionat {selectedCount} de les versions {itemCount}", + "SettingsLogRotate": "Rotació del registre", + "AreYouSureYouWantToDeleteCategory": "Esteu segur que voleu suprimir la categoria assignada?", + "Book": "Llibre" } diff --git a/src/NzbDrone.Core/Localization/Core/cs.json b/src/NzbDrone.Core/Localization/Core/cs.json index 2b5f73f97..21975a02e 100644 --- a/src/NzbDrone.Core/Localization/Core/cs.json +++ b/src/NzbDrone.Core/Localization/Core/cs.json @@ -1,7 +1,7 @@ { "Add": "Přidat", - "CertificateValidation": "Ověření certifikátu", - "DeleteBackupMessageText": "Opravdu chcete odstranit zálohu '{name}'?", + "CertificateValidation": "Ověřování certifikátu", + "DeleteBackupMessageText": "Opravdu chcete odstranit zálohu ‚{name}‘?", "YesCancel": "Ano, zrušit", "About": "O aplikaci", "Component": "Komponenta", @@ -18,12 +18,12 @@ "Usenet": "Usenet", "AddDownloadClient": "Přidat klienta pro stahování", "Backups": "Zálohy", - "CancelPendingTask": "Opravdu chcete zrušit tento nevyřízený úkol?", + "CancelPendingTask": "Opravdu chcete zrušit tento úkol čekající na vyřízení?", "MovieIndexScrollBottom": "Rejstřík filmů: Posun dolů", "ProxyType": "Typ serveru proxy", "Reddit": "Reddit", "ErrorLoadingContents": "Chyba při načítání obsahu", - "IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Všechny indexery nejsou k dispozici z důvodu selhání po dobu delší než 6 hodin", + "IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Všechny indexery jsou nedostupné z důvodu selhání déle než 6 hodin", "RemovedFromTaskQueue": "Odebráno z fronty úkolů", "ResetAPIKey": "Resetovat klíč API", "SSLCertPassword": "Heslo SSL Cert", @@ -35,27 +35,27 @@ "Warn": "Varovat", "Wiki": "Wiki", "Connections": "Připojení", - "DeleteDownloadClientMessageText": "Opravdu chcete odstranit klienta pro stahování '{name}'?", - "Details": "Detaily", + "DeleteDownloadClientMessageText": "Opravdu chcete odstranit klienta pro stahování ‚{name}‘?", + "Details": "Podrobnosti", "Disabled": "Zakázáno", - "Docker": "Přístavní dělník", + "Docker": "Docker", "Donations": "Dary", - "DownloadClientSettings": "Stáhněte si nastavení klienta", - "DownloadClientStatusAllClientHealthCheckMessage": "Všichni klienti pro stahování nejsou kvůli chybám k dispozici", - "DownloadClientStatusSingleClientHealthCheckMessage": "Stahování klientů není k dispozici z důvodu selhání: {downloadClientNames}", + "DownloadClientSettings": "Nastavení klienta pro stahování", + "DownloadClientStatusAllClientHealthCheckMessage": "Všichni klienti pro stahování jsou nedostupní z důvodu selhání", + "DownloadClientStatusSingleClientHealthCheckMessage": "Klienti pro stahování jsou nedostupní z důvodu selhání: {downloadClientNames}", "Folder": "Složka", - "Grabs": "Urvat", - "HealthNoIssues": "Žádné problémy s vaší konfigurací", + "Grabs": "Získané", + "NoIssuesWithYourConfiguration": "Žádné problémy s vaší konfigurací", "HideAdvanced": "Skrýt pokročilé", "Host": "Hostitel", "Hostname": "Název hostitele", - "IncludeHealthWarningsHelpText": "Zahrnout zdravotní varování", + "IncludeHealthWarningsHelpText": "Včetně varování ohledně zdraví", "Indexer": "Indexer", "IndexerFlags": "Příznaky indexeru", - "IndexerPriority": "Priorita indexování", - "IndexerPriorityHelpText": "Priorita indexování od 1 (nejvyšší) do 50 (nejnižší). Výchozí: 25.", + "IndexerPriority": "Priorita indexeru", + "IndexerPriorityHelpText": "Priorita indexeru od 1 (Nejvyšší) do 50 (Nejnižší). Výchozí: 25.", "Indexers": "Indexery", - "IndexerStatusAllUnavailableHealthCheckMessage": "Všechny indexery nejsou k dispozici z důvodu selhání", + "IndexerStatusAllUnavailableHealthCheckMessage": "Všechny indexery jsou nedostupné z důvodu selhání", "LastWriteTime": "Čas posledního zápisu", "Level": "Úroveň", "LogLevel": "Úroveň protokolu", @@ -69,23 +69,23 @@ "UnselectAll": "Odznačit vše", "UpdateStartupNotWritableHealthCheckMessage": "Aktualizaci nelze nainstalovat, protože spouštěcí složku „{startupFolder}“ nelze zapisovat uživatelem „{userName}“.", "Version": "Verze", - "AnalyticsEnabledHelpText": "Odesílejte anonymní informace o použití a chybách na servery {appName}u. To zahrnuje informace o vašem prohlížeči, které stránky {appName} WebUI používáte, hlášení chyb a také verzi operačního systému a běhového prostředí. Tyto informace použijeme k upřednostnění funkcí a oprav chyb.", + "AnalyticsEnabledHelpText": "Odesílejte anonymní informace o použití a chybách na servery {appName}u. To zahrnuje informace o vašem prohlížeči, které stránky webového rozhraní {appName}u používáte, hlášení chyb a také verzi operačního systému a běhového prostředí. Tyto informace použijeme k určení priorit funkcí a oprav chyb.", "ApiKey": "Klíč API", "AppDataDirectory": "Adresář AppData", "AppDataLocationHealthCheckMessage": "Aktualizace nebude možná, aby se zabránilo odstranění AppData při aktualizaci", - "ApplicationStatusCheckAllClientMessage": "Všechny seznamy nejsou k dispozici z důvodu selhání", - "ApplicationStatusCheckSingleClientMessage": "Seznamy nejsou k dispozici z důvodu selhání: {0}", + "ApplicationStatusCheckAllClientMessage": "Všechny aplikace jsou nedostupné z důvodu selhání", + "ApplicationStatusCheckSingleClientMessage": "Aplikace nedostupné z důvodu selhání: {0}", "Apply": "Použít", "Branch": "Větev", - "BranchUpdate": "Pobočka, která se má použít k aktualizaci {appName}", - "EditIndexer": "Upravit indexátor", + "BranchUpdate": "Větev použitá k aktualizaci {appName}u", + "EditIndexer": "Upravit indexer", "ForMoreInformationOnTheIndividualDownloadClients": "Další informace o jednotlivých klientech pro stahování získáte kliknutím na informační tlačítka.", - "General": "Všeobecné", - "CloseCurrentModal": "Zavřít aktuální modální", + "General": "Obecné", + "CloseCurrentModal": "Zavřít aktuální modální okno", "Columns": "Sloupce", - "ConnectionLost": "Spojení ztraceno", + "ConnectionLost": "Ztráta spojení", "ConnectSettings": "Nastavení připojení", - "Custom": "Zvyk", + "Custom": "Vlastní", "Error": "Chyba", "Failed": "Selhalo", "FeatureRequests": "Žádosti o funkce", @@ -93,9 +93,9 @@ "Files": "Soubory", "Filter": "Filtr", "Fixed": "Pevný", - "FocusSearchBox": "Zaostřovací vyhledávací pole", + "FocusSearchBox": "Zaměřit vyhledávací pole", "GeneralSettingsSummary": "Port, SSL, uživatelské jméno / heslo, proxy, analytika a aktualizace", - "History": "Dějiny", + "History": "Historie", "HomePage": "Domovská stránka", "SettingsEnableColorImpairedModeHelpText": "Upravený styl umožňující uživatelům s barevným postižením lépe rozlišovat barevně kódované informace", "SettingsLongDateFormat": "Long Date Format", @@ -105,8 +105,8 @@ "Tasks": "Úkoly", "Test": "Test", "UnableToLoadTags": "Značky nelze načíst", - "IndexerProxyStatusAllUnavailableHealthCheckMessage": "Všechny indexery nejsou k dispozici z důvodu selhání", - "ApplyTags": "Použít značky", + "IndexerProxyStatusAllUnavailableHealthCheckMessage": "Všechny proxy indexerů jsou nedostupné z důvodu selhání", + "ApplyTags": "Použít štítky", "MoreInfo": "Více informací", "System": "Systém", "Enabled": "Povoleno", @@ -121,7 +121,7 @@ "NoLinks": "Žádné odkazy", "Presets": "Předvolby", "Priority": "Přednost", - "Grabbed": "Popadl", + "Grabbed": "Získáno", "Health": "Zdraví", "LogLevelTraceHelpTextWarning": "Trasování protokolování by mělo být povoleno pouze dočasně", "ProxyBadRequestHealthCheckMessage": "Nepodařilo se otestovat proxy. StatusCode: {statusCode}", @@ -171,19 +171,19 @@ "UseProxy": "Použij proxy", "Username": "Uživatelské jméno", "Yesterday": "Včera", - "AutomaticSearch": "Vyhledat automaticky", - "BackupFolderHelpText": "Relativní cesty budou v adresáři AppData společnosti {appName}", + "AutomaticSearch": "Automatické vyhledávání", + "BackupFolderHelpText": "Relativní cesty budou v adresáři AppData {appName}u", "BackupIntervalHelpText": "Interval mezi automatickými zálohami", - "BackupNow": "Ihned zálohovat", + "BackupNow": "Zálohovat nyní", "BackupRetentionHelpText": "Automatické zálohy starší než doba uchovávání budou automaticky vyčištěny", - "BeforeUpdate": "Před zálohováním", + "BeforeUpdate": "Před aktualizací", "BindAddress": "Vázat adresu", - "BindAddressHelpText": "Platná IP adresa, localhost nebo '*' pro všechna rozhraní", - "BranchUpdateMechanism": "Větev používaná externím aktualizačním mechanismem", + "BindAddressHelpText": "Platná IP adresa, localhost nebo ‚*‘ pro všechna rozhraní", + "BranchUpdateMechanism": "Větev použitá externím aktualizačním mechanismem", "BypassProxyForLocalAddresses": "Obcházení proxy serveru pro místní adresy", - "DeleteIndexerProxyMessageText": "Opravdu chcete smazat značku „{0}“?", - "DeleteTag": "Smazat značku", - "IndexerProxyStatusUnavailableHealthCheckMessage": "Indexery nedostupné z důvodu selhání: {indexerProxyNames}", + "DeleteIndexerProxyMessageText": "Opravdu chcete odstranit proxy indexeru ‚{name}‘?", + "DeleteTag": "Odstranit štítek", + "IndexerProxyStatusUnavailableHealthCheckMessage": "Proxy indexerů nedostupné z důvodu selhání: {indexerProxyNames}", "Name": "název", "New": "Nový", "Protocol": "Protokol", @@ -198,12 +198,12 @@ "SSLCertPasswordHelpText": "Heslo pro soubor pfx", "SSLCertPath": "Cesta certifikátu SSL", "SSLCertPathHelpText": "Cesta k souboru pfx", - "UnableToLoadBackups": "Nelze načíst zálohy", + "BackupsLoadError": "Nelze načíst zálohy", "DownloadClientsLoadError": "Nelze načíst klienty pro stahování", "UnableToLoadGeneralSettings": "Nelze načíst obecná nastavení", - "DeleteNotification": "Smazat oznámení", + "DeleteNotification": "Odstranit oznámení", "EnableAutomaticSearch": "Povolit automatické vyhledávání", - "EnableInteractiveSearchHelpText": "Bude použito při použití interaktivního vyhledávání", + "EnableInteractiveSearchHelpText": "Použije se při interaktivním vyhledávání", "GeneralSettings": "Obecné nastavení", "InteractiveSearch": "Interaktivní vyhledávání", "Interval": "Interval", @@ -221,51 +221,51 @@ "Restore": "Obnovit", "SettingsShowRelativeDates": "Zobrazit relativní data", "SettingsShowRelativeDatesHelpText": "Zobrazit relativní (dnes / včera / atd.) Nebo absolutní data", - "SystemTimeCheckMessage": "Systémový čas je vypnutý o více než 1 den. Naplánované úlohy nemusí fungovat správně, dokud nebude čas opraven", - "AddingTag": "Přidání značky", + "SystemTimeHealthCheckMessage": "Systémový čas je vypnutý o více než 1 den. Naplánované úlohy nemusí fungovat správně, dokud nebude čas opraven", + "AddingTag": "Přidávání štítku", "Age": "Stáří", "All": "Vše", - "AllIndexersHiddenDueToFilter": "Všechny filmy jsou skryty kvůli použitému filtru.", + "AllIndexersHiddenDueToFilter": "Všechny indexery jsou skryty kvůli použitému filtru.", "Analytics": "Analýzy", "EnableRss": "Povolit RSS", "NoChange": "Žádná změna", "Authentication": "Ověřování", - "AuthenticationMethodHelpText": "Vyžadovat uživatelské jméno a heslo pro přístup k {appName}", + "AuthenticationMethodHelpText": "Vyžadovat uživatelské jméno a heslo pro přístup k {appName}u", "Automatic": "Automatický", "Backup": "Záloha", "Cancel": "Zrušit", - "CertificateValidationHelpText": "Změňte, jak přísné je ověření certifikace HTTPS", + "CertificateValidationHelpText": "Změňte přísnost ověřování certifikace HTTPS", "ChangeHasNotBeenSavedYet": "Změna ještě nebyla uložena", - "Clear": "Vyčistit", + "Clear": "Vymazat", "ClientPriority": "Priorita klienta", "CloneProfile": "Klonovat profil", "Close": "Zavřít", "CouldNotConnectSignalR": "Nelze se připojit k SignalR, uživatelské rozhraní se neaktualizuje", "CustomFilters": "Vlastní filtry", - "Date": "datum", - "Dates": "Termíny", + "Date": "Datum", + "Dates": "Data", "DatabaseMigration": "Migrace databáze", - "Delete": "Vymazat", - "DeleteApplicationMessageText": "Opravdu chcete smazat oznámení „{0}“?", + "Delete": "Odstranit", + "DeleteApplicationMessageText": "Opravdu chcete odstranit aplikaci ‚{name}‘?", "DeleteBackup": "Odstranit zálohu", - "DeleteDownloadClient": "Odstranit staženého klienta", - "DeleteNotificationMessageText": "Opravdu chcete smazat oznámení '{name}'?", - "DeleteTagMessageText": "Opravdu chcete smazat značku „{0}“?", - "Discord": "Svár", - "DownloadClient": "Stáhnout klienta", - "DownloadClients": "Stáhnout klienty", + "DeleteDownloadClient": "Odstranit klienta pro stahování", + "DeleteNotificationMessageText": "Opravdu chcete odstranit oznámení ‚{name}‘?", + "DeleteTagMessageText": "Opravdu chcete odstranit štítek ‚{label}‘?", + "Discord": "Discord", + "DownloadClient": "Klient pro stahování", + "DownloadClients": "Klienti pro stahování", "Edit": "Upravit", - "Enable": "Umožnit", - "EnableAutomaticSearchHelpText": "Použije se, když se automatické vyhledávání provádí pomocí uživatelského rozhraní nebo {appName}", + "Enable": "Povolit", + "EnableAutomaticSearchHelpText": "Použije se při automatickém vyhledávání prostřednictvím uživatelského rozhraní nebo pomocí {appName}", "EnableInteractiveSearch": "Povolit interaktivní vyhledávání", "EnableSSL": "Povolit SSL", "EnableSslHelpText": " Vyžaduje restartování spuštěné jako správce, aby se projevilo", "Events": "Události", "EventType": "Typ události", "Exception": "Výjimka", - "ExistingTag": "Stávající značka", + "ExistingTag": "Stávající štítek", "IllRestartLater": "Restartuji později", - "IndexerLongTermStatusUnavailableHealthCheckMessage": "Indexery nedostupné z důvodu selhání po dobu delší než 6 hodin: {indexerNames}", + "IndexerLongTermStatusUnavailableHealthCheckMessage": "Indexery nedostupné z důvodu selhání déle než 6 hodin: {indexerNames}", "IndexerStatusUnavailableHealthCheckMessage": "Indexery nedostupné z důvodu selhání: {indexerNames}", "SettingsTimeFormat": "Časový formát", "ShowAdvanced": "Zobrazit pokročilé", @@ -307,9 +307,9 @@ "UnsavedChanges": "Neuložené změny", "UpdateAutomaticallyHelpText": "Automaticky stahovat a instalovat aktualizace. Stále budete moci instalovat ze systému: Aktualizace", "NetCore": ".NET Core", - "Filters": "Filtr", - "HistoryCleanupDaysHelpText": "Nastavením na 0 zakážete automatické čištění", - "HistoryCleanupDaysHelpTextWarning": "Soubory v koši starší než vybraný počet dní budou automaticky vyčištěny", + "Filters": "Filtry", + "HistoryCleanupDaysHelpText": "Nastavte na 0 pro zakázání automatického čištění", + "HistoryCleanupDaysHelpTextWarning": "Položky historie starší než vybraný počet dní se vyčistí automaticky", "MaintenanceRelease": "Údržbové vydání: opravy chyb a další vylepšení. Další podrobnosti najdete v GitHub Commit History", "OnGrab": "Chyť", "OnHealthIssue": "K otázce zdraví", @@ -319,59 +319,59 @@ "No": "Ne", "UnableToLoadIndexers": "Nelze načíst indexery", "Yes": "Ano", - "GrabReleases": "Uchopte uvolnění", - "ApplicationLongTermStatusCheckSingleClientMessage": "Indexery nedostupné z důvodu selhání po dobu delší než 6 hodin: {0}", - "ApplicationLongTermStatusCheckAllClientMessage": "Všechny indexery nejsou k dispozici z důvodu selhání po dobu delší než 6 hodin", - "Ended": "Skončil", + "GrabReleases": "Získat vydání", + "ApplicationLongTermStatusCheckSingleClientMessage": "Aplikace nedostupné z důvodu selhání déle než 6 hodin: {0}", + "ApplicationLongTermStatusCheckAllClientMessage": "Všechny aplikace jsou nedostupné z důvodu selhání déle než 6 hodin", + "Ended": "Ukončeno", "LastDuration": "lastDuration", "LastExecution": "Poslední poprava", "NextExecution": "Další spuštění", "Queued": "Ve frontě", "Remove": "Odstranit", "Replace": "Nahradit", - "TheLatestVersionIsAlreadyInstalled": "Nejnovější verze aplikace {appName} je již nainstalována", + "OnLatestVersion": "Nejnovější verze aplikace {appName} je již nainstalována", "More": "Více", - "ApplyTagsHelpTextAdd": "Přidat: Přidá značky k již existujícímu seznamu", - "ApplyTagsHelpTextHowToApplyApplications": "Jak použít značky na vybrané filmy", - "DeleteSelectedDownloadClients": "Odstranit klienta pro stahování", - "DeleteSelectedIndexersMessageText": "Opravdu chcete smazat {count} vybraný(ch) indexer(ů)?", - "DeleteSelectedApplicationsMessageText": "Opravdu chcete odstranit indexer „{0}“?", - "DeleteSelectedDownloadClientsMessageText": "Opravdu chcete smazat {count} vybraných klientů pro stahování?", + "ApplyTagsHelpTextAdd": "Přidat: Přidat štítky do existujícího seznamu štítků", + "ApplyTagsHelpTextHowToApplyApplications": "Jak použít štítky na vybrané aplikace", + "DeleteSelectedDownloadClients": "Odstranit klienty pro stahování", + "DeleteSelectedIndexersMessageText": "Opravdu chcete odstranit {count} vybraných indexerů?", + "DeleteSelectedApplicationsMessageText": "Opravdu chcete odstranit {count} vybraných aplikací?", + "DeleteSelectedDownloadClientsMessageText": "Opravdu chcete odstranit {count} vybraných klientů pro stahování?", "Year": "Rok", - "ApplyTagsHelpTextRemove": "Odebrat: Odebrat zadané značky", - "DownloadClientPriorityHelpText": "Upřednostněte více klientů pro stahování. Round-Robin se používá pro klienty se stejnou prioritou.", - "ApplyTagsHelpTextHowToApplyIndexers": "Jak použít značky na vybrané indexery", - "ApplyTagsHelpTextReplace": "Nahradit: Nahradit značky zadanými značkami (prázdné pole vymaže všechny značky)", + "ApplyTagsHelpTextRemove": "Odebrat: Odebrat zadané štítky", + "DownloadClientPriorityHelpText": "Upřednostněte více klientů pro stahování. Pro klienty se stejnou prioritou se používá funkce Round-Robin.", + "ApplyTagsHelpTextHowToApplyIndexers": "Jak použít štítky na vybrané indexery", + "ApplyTagsHelpTextReplace": "Nahradit: Nahradit štítky zadanými štítky (prázdné pole vymaže všechny štítky)", "Track": "Stopa", - "Genre": "Žánry", + "Genre": "Žánr", "ConnectionLostReconnect": "{appName} se pokusí připojit automaticky, nebo můžete kliknout na tlačítko znovunačtení níže.", "RecentChanges": "Nedávné změny", "WhatsNew": "Co je nového?", - "DeleteAppProfileMessageText": "Opravdu chcete smazat kvalitní profil {0}", - "ConnectionLostToBackend": "{appName} ztratil spojení s backendem a pro obnovení funkčnosti bude třebaho znovu načíst.", + "DeleteAppProfileMessageText": "Opravdu chcete odstranit profil aplikace ‚{name}‘?", + "ConnectionLostToBackend": "{appName} ztratil spojení s backendem a pro obnovení funkčnosti bude potřeba ho znovu načíst.", "minutes": "Minut", "ApplicationURL": "URL aplikace", - "ApplicationUrlHelpText": "Externí adresa URL této aplikace včetně http(s)://, portu a základní adresy URL", + "ApplicationUrlHelpText": "Externí adresa URL této aplikace včetně http(s)://, portu a základu URL", "ApplyChanges": "Použít změny", "ApiKeyValidationHealthCheckMessage": "Aktualizujte svůj klíč API tak, aby měl alespoň {length} znaků. Můžete to provést prostřednictvím nastavení nebo konfiguračního souboru", "AppUpdated": "{appName} aktualizován", - "AddDownloadClientImplementation": "Přidat klienta pro stahování - {implementationName}", - "AuthenticationRequired": "Vyžadované ověření", - "AuthenticationRequiredHelpText": "Změnit, pro které požadavky je vyžadováno ověření. Pokud nerozumíte rizikům, neměňte je.", + "AddDownloadClientImplementation": "Přidat klienta pro stahování – {implementationName}", + "AuthenticationRequired": "Vyžadováno ověření", + "AuthenticationRequiredHelpText": "Změnit, pro které požadavky je vyžadováno ověření. Neměňte, pokud nerozumíte rizikům.", "AddCustomFilter": "Přidat vlastní filtr", "AddConnection": "Přidat spojení", - "AddConnectionImplementation": "Přidat spojení - {implementationName}", - "AddIndexerImplementation": "Přidat indexer - {implementationName}", + "AddConnectionImplementation": "Přidat spojení – {implementationName}", + "AddIndexerImplementation": "Přidat indexer – {implementationName}", "Publisher": "Vydavatel", "Categories": "Kategorie", "Notification": "Oznámení", - "AddApplicationImplementation": "Přidat spojení - {implementationName}", - "AddIndexerProxyImplementation": "Přidat indexátor - {implementationName}", + "AddApplicationImplementation": "Přidat aplikaci – {implementationName}", + "AddIndexerProxyImplementation": "Přidat proxy server indexeru – {implementationName}", "Artist": "Umělec", "EditIndexerImplementation": "Upravit indexer - {implementationName}", - "Episode": "epizoda", - "NotificationStatusAllClientHealthCheckMessage": "Všechny seznamy nejsou k dispozici z důvodu selhání", - "NotificationStatusSingleClientHealthCheckMessage": "Seznamy nejsou k dispozici z důvodu selhání: {notificationNames}", + "Episode": "Epizoda", + "NotificationStatusAllClientHealthCheckMessage": "Všechna oznámení jsou nedostupná z důvodu selhání", + "NotificationStatusSingleClientHealthCheckMessage": "Oznámení nedostupná z důvodu selhání: {notificationNames}", "Application": "Aplikace", "AppUpdatedVersion": "{appName} byl aktualizován na verzi `{version}`, abyste získali nejnovější změny, musíte znovu načíst {appName}", "Encoding": "Kódování", @@ -382,35 +382,262 @@ "Album": "Album", "Applications": "Aplikace", "Connect": "Oznámení", - "EditConnectionImplementation": "Přidat spojení - {implementationName}", + "EditConnectionImplementation": "Upravit připojení - {implementationName}", "EditDownloadClientImplementation": "Upravit klienta pro stahování - {implementationName}", "AuthForm": "Formuláře (přihlašovací stránka)", "Clone": "Klonovat", "DefaultNameCopiedProfile": "{name} - Kopírovat", "DisabledForLocalAddresses": "Zakázáno pro místní adresy", - "EditApplicationImplementation": "Přidat spojení - {implementationName}", + "EditApplicationImplementation": "Upravit aplikaci - {implementationName}", "None": "Žádný", "ResetAPIKeyMessageText": "Opravdu chcete resetovat klíč API?", "Database": "Databáze", - "CountDownloadClientsSelected": "{count} vybraných klientů ke stahování", + "CountDownloadClientsSelected": "{count} vybraných klientů pro stahování", "CountIndexersSelected": "{count} vybraných indexerů", - "EditIndexerProxyImplementation": "Přidat indexátor - {implementationName}", + "EditIndexerProxyImplementation": "Upravit proxy indexeru - {implementationName}", "AuthBasic": "Základní (vyskakovací okno prohlížeče)", - "AuthenticationRequiredWarning": "Aby se zabránilo vzdálenému přístupu bez ověření, vyžaduje nyní {appName} povolení ověření. Ověřování z místních adres můžete volitelně zakázat.", + "AuthenticationRequiredWarning": "Aby se zabránilo vzdálenému přístupu bez ověření, vyžaduje nyní {appName}, aby bylo povoleno ověřování. Volitelně můžete zakázat ověřování z místních adres.", "RestartProwlarr": "Restartujte {appName}", "Duration": "Trvání", "EditSelectedDownloadClients": "Upravit vybrané klienty pro stahování", "EditSelectedIndexers": "Upravit vybrané indexery", "AuthenticationMethod": "Metoda ověřování", - "AuthenticationRequiredPasswordHelpTextWarning": "Vložte nové heslo", - "AuthenticationRequiredUsernameHelpTextWarning": "Vložte nové uživatelské jméno", - "AuthenticationMethodHelpTextWarning": "Prosím vyberte platnou metodu ověřování", + "AuthenticationRequiredPasswordHelpTextWarning": "Zadejte nové heslo", + "AuthenticationRequiredUsernameHelpTextWarning": "Zadejte nové uživatelské jméno", + "AuthenticationMethodHelpTextWarning": "Vyberte platnou metodu ověřování", "AuthenticationRequiredPasswordConfirmationHelpTextWarning": "Potvrďte nové heslo", "days": "dnů", "Id": "ID", - "CountApplicationsSelected": "Vybráno {0} kolekcí", - "IndexerHDBitsSettingsCodecs": "Kodek", + "CountApplicationsSelected": "{count} vybraných aplikací", + "IndexerHDBitsSettingsCodecs": "Kodeky", "IndexerHDBitsSettingsMediums": "Střední", "Directory": "Adresář", - "CustomFilter": "Vlastní filtry" + "CustomFilter": "Vlastní filtr", + "ProxyValidationBadRequest": "Nepodařilo se otestovat proxy. StatusCode: {statusCode}", + "Default": "Výchozí", + "GrabRelease": "Získat vydání", + "Category": "Kategorie", + "BlackholeFolderHelpText": "Složka, do které {appName} uloží soubor {extension}", + "DownloadClientSettingsUrlBaseHelpText": "Přidá předponu k url {clientName}, například {url}", + "Any": "Jakákoliv", + "BuiltIn": "Vestavěný", + "Script": "Skript", + "PublishedDate": "Datum zveřejnění", + "AllSearchResultsHiddenByFilter": "Všechny výsledky vyhledávání jsou skryty použitým filtrem.", + "DockerUpdater": "Aktualizujte kontejner dockeru, abyste získali aktualizaci", + "Download": "Stažení", + "ErrorRestoringBackup": "Chyba při obnovování zálohy", + "ExternalUpdater": "{appName} je nakonfigurován pro použití externího aktualizačního mechanismu", + "FailedToFetchUpdates": "Nepodařilo se načíst aktualizace", + "NoEventsFound": "Nebyly nalezeny žádné události", + "RestartReloadNote": "Poznámka: {appName} se během procesu obnovy automaticky restartuje a znovu načte uživatelské rozhraní.", + "UpdateAppDirectlyLoadError": "{appName} nelze aktualizovat přímo,", + "AptUpdater": "K instalaci aktualizace používat apt", + "InstallLatest": "Nainstalujte nejnovější", + "Stats": "Postavení", + "CurrentlyInstalled": "Aktuálně nainstalováno", + "Mixed": "Pevný", + "ActiveIndexers": "Aktivní indexery", + "ActiveApps": "Aktivní aplikace", + "AppSettingsSummary": "Aplikace a nastavení pro konfiguraci interakce {appName}u s vašimi programy PVR", + "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "Pokud je torrent blokován pomocí hash, nemusí být u některých indexerů správně odmítnut během RSS/vyhledávání. Povolení této funkce umožní jeho odmítnutí po zachycení torrentu, ale před jeho odesláním klientovi.", + "ApplicationsLoadError": "Nelze načíst seznam aplikací", + "AppProfileInUse": "Používaný profil aplikace", + "AppsMinimumSeedersHelpText": "Minimální počet seederů požadovaných aplikacemi pro indexer, výchozí hodnota synchronizačního profilu je prázdná", + "AverageGrabs": "Průměrné získání", + "AverageQueries": "Průměrné dotazy", + "AdvancedSettingsShownClickToHide": "Rozšířená nastavení jsou zobrazená, klikněte pro skrytí", + "AdvancedSettingsHiddenClickToShow": "Rozšířená nastavení jsou skrytá, klikněte pro zobrazení", + "AppsMinimumSeeders": "Minimální počet seederů aplikací", + "AddNewIndexer": "Přidat nový indexer", + "AddToDownloadClient": "Přidat vydání do klienta pro stahování", + "AddIndexerProxy": "Přidat proxy server indexeru", + "AppProfileSelectHelpText": "Profily aplikace slouží k ovládání nastavení RSS, automatického vyhledávání a interaktivního vyhledávání při synchronizaci aplikace", + "BookSearch": "Vyhledávání knihy", + "ClearHistory": "Vymazat historii", + "Auth": "Ověřování", + "ConnectSettingsSummary": "Oznámení a vlastní skripty", + "AreYouSureYouWantToDeleteIndexer": "Opravdu chcete odstranit ‚{name}‘ z {appName}u?", + "AuthQueries": "Ověřovací dotazy", + "CountIndexersAvailable": "{count} dostupných indexerů", + "ApplicationTagsHelpText": "Synchronizovat s touto aplikací indexery, které mají jeden nebo více shodných štítků. Pokud zde nejsou uvedeny žádné štítky, nebude synchronizace žádných indexerů znemožněna kvůli jejich štítkům.", + "ApplicationTagsHelpTextWarning": "Štítky je potřeba používat opatrně, mohou mít nechtěné účinky. Aplikace se štítkem se bude synchronizovat pouze s indexery se stejným štítkem.", + "BasicSearch": "Základní vyhledávání", + "ClearHistoryMessageText": "Opravdu chcete vymazat celou historii {appName}u?", + "AddDownloadClientToProwlarr": "Přidání klienta pro stahování umožňuje {appName} odesílat vydání přímo z uživatelského rozhraní při ručním vyhledávání.", + "AddRemoveOnly": "Pouze přidat a odebrat", + "AudioSearch": "Vyhledávání audia", + "ApplicationSettingsSyncRejectBlocklistedTorrentHashes": "Synchronizovat odmítnuté blokované hashe torrentů při získávání", + "Apps": "Aplikace", + "ClickToChangeQueryOptions": "Kliknutím změníte možnosti dotazu", + "Author": "Autor", + "AverageResponseTimesMs": "Průměrné doby odezvy indexerů (ms)", + "Book": "Kniha", + "BookSearchTypes": "Typy vyhledávání knihy", + "AddApplication": "Přidat aplikaci", + "AddSyncProfile": "Přidat synchronizační profil", + "AddedToDownloadClient": "Vydání přidáno do klienta", + "AddCategory": "Přidat kategorii", + "AreYouSureYouWantToDeleteCategory": "Opravdu chcete odstranit namapovanou kategorii?", + "DownloadClientRTorrentSettingsUrlPath": "Cesta URL", + "DefaultCategory": "Výchozí kategorie", + "DownloadClientFloodSettingsUrlBaseHelpText": "Přidá předponu do Flood API, například {url}", + "DownloadClientFreeboxSettingsApiUrl": "URL API", + "DownloadClientSettingsInitialState": "Počáteční stav", + "DownloadClientSettingsPriorityItemHelpText": "Priorita použitá při získávání položek", + "FailedToFetchSettings": "Nepodařilo se načíst nastavení", + "GrabTitle": "Získat název", + "DownloadClientNzbgetSettingsAddPausedHelpText": "Tato volba vyžaduje NzbGet verze alespoň 16.0", + "EnableRssHelpText": "Povolit kanál RSS pro indexer", + "DeleteApplication": "Odstranit aplikaci", + "DeleteSelectedApplications": "Odstranit vybrané aplikace", + "DeleteSelectedIndexers": "Odstranit vybrané indexery", + "DevelopmentSettings": "Nastavení pro vývoj", + "DisabledUntil": "Zakázáno do", + "DownloadClientCategory": "Kategorie klienta pro stahování", + "DownloadClientDelugeSettingsUrlBaseHelpText": "Přidá předponu do url adresy json deluge, viz {url}", + "DownloadClientDownloadStationSettingsDirectoryHelpText": "Volitelná sdílená složka pro stahování, ponechte prázdné pro použití výchozího umístění Download Station", + "DownloadClientFloodSettingsAdditionalTags": "Další štítky", + "DownloadClientFloodSettingsAdditionalTagsHelpText": "Přidá vlastnosti médií jako štítky. Nápovědy jsou příklady.", + "DownloadClientFloodSettingsTagsHelpText": "Počáteční štítky stahování. Aby bylo stahování rozpoznáno, musí mít všechny počáteční štítky. Tím se zabrání konfliktům s nesouvisejícími stahováními.", + "DownloadClientFreeboxSettingsAppId": "ID aplikace", + "DownloadClientFreeboxSettingsAppToken": "Token aplikace", + "DownloadClientFreeboxSettingsAppTokenHelpText": "Token aplikace získaný při vytváření přístupu k Freebox API (tj. ‚app_token‘)", + "DownloadClientFreeboxSettingsHostHelpText": "Název hostitele nebo IP adresa hostitele Freeboxu, výchozí hodnota je ‚{url}‘ (funguje pouze ve stejné síti)", + "DownloadClientFreeboxSettingsPortHelpText": "Port použitý pro přístup k rozhraní Freeboxu, výchozí hodnota je ‚{port}‘", + "DownloadClientPneumaticSettingsNzbFolder": "Složka Nzb", + "DownloadClientPneumaticSettingsNzbFolderHelpText": "Tato složka bude muset být dostupná z XBMC", + "DownloadClientQbittorrentSettingsContentLayout": "Rozvržení obsahu", + "DownloadClientQbittorrentSettingsSequentialOrderHelpText": "Stahovat v postupném pořadí (qBittorrent 4.1.0+)", + "DownloadClientQbittorrentSettingsSequentialOrder": "Postupné pořadí", + "DownloadClientRTorrentSettingsAddStoppedHelpText": "Povolení přidá torrenty a magnety do rTorrentu v zastaveném stavu. To může způsobit poškození souborů magnet.", + "DownloadClientRTorrentSettingsDirectoryHelpText": "Volitelné umístění pro stahování, ponechte prázdné pro použití výchozího umístění rTorrentu", + "DownloadClientSettingsAddPaused": "Přidat pozastavené", + "DownloadClientSettingsDefaultCategorySubFolderHelpText": "Výchozí záložní kategorie, pokud pro vydání neexistuje žádná namapovaná kategorie. Přidáním kategorie specifické pro {appName} se zabrání konfliktům s nesouvisejícími stahováními, která nejsou {appName}. Použití kategorie je nepovinné, ale důrazně se doporučuje. Vytvoří podadresář [kategorie] ve výstupním adresáři.", + "DownloadClientSettingsUseSslHelpText": "Při připojení k {clientName} použít zabezpečené připojení", + "DownloadClientTransmissionSettingsDirectoryHelpText": "Volitelné umístění pro stahování, ponechte prázdné pro použití výchozího umístění Transmission", + "DownloadClientTransmissionSettingsUrlBaseHelpText": "Přidá předponu k url {clientName} rpc, např. {url}, výchozí hodnota je ‚{defaultUrl}‘", + "DownloadClientsSettingsSummary": "Konfigurace klientů pro stahování pro integraci do vyhledávání v uživatelském rozhraní {appName}", + "ElapsedTime": "Uplynulý čas", + "EnableIndexer": "Povolit indexer", + "External": "Externí", + "FullSync": "Úplná synchronizace", + "HealthMessagesInfoBox": "Další informace o příčině těchto zpráv o kontrole zdraví najdete kliknutím na odkaz wiki (ikona knihy) na konci řádku nebo kontrolou [logů]({link}). Pokud máte potíže s interpretací těchto zpráv, můžete se obrátit na naši podporu, a to na níže uvedených odkazech.", + "Implementation": "Implementace", + "DeleteClientCategory": "Odstranit kategorii klienta pro stahování", + "DownloadClientRTorrentSettingsAddStopped": "Přidat zastavené", + "DeleteIndexerProxy": "Odstranit proxy indexerů", + "Description": "Popis", + "IncludeManualGrabsHelpText": "Včetně ručních získání provedených v {appName}", + "GoToApplication": "Přejít na aplikaci", + "DownloadClientAriaSettingsDirectoryHelpText": "Volitelné umístění pro stahování, ponechte prázdné pro použití výchozího umístění Aria2", + "DownloadClientPneumaticSettingsStrmFolderHelpText": "Soubory .strm v této složce budou importovány pomocí drone", + "Destination": "Cíl", + "DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText": "Stahovat nejprve první a poslední kusy (qBittorrent 4.1.0+)", + "DeleteSelectedIndexer": "Odstranit vybraný indexer", + "DownloadClientPneumaticSettingsStrmFolder": "Složka Strm", + "DownloadClientQbittorrentSettingsFirstAndLastFirst": "Nejprve první a poslední", + "DownloadClientFreeboxSettingsApiUrlHelpText": "Definujte základní adresu URL Freebox API s verzí API, např. ‚{url}‘, výchozí hodnota je ‚{defaultApiUrl}‘", + "HistoryCleanup": "Vyčištění historie", + "DownloadClientFreeboxSettingsAppIdHelpText": "ID aplikace zadané při vytváření přístupu k Freebox API (tj. ‚app_id‘)", + "DownloadClientQbittorrentSettingsContentLayoutHelpText": "Zda použít rozvržení obsahu nakonfigurované v qBittorrentu, původní rozvržení z torrentu nebo vždy vytvořit podsložku (qBittorrent 4.3.2+)", + "DownloadClientQbittorrentSettingsInitialStateHelpText": "Počáteční stav torrentů přidaných do qBittorrentu. Pamatujte, že vynucené torrenty nedodržují omezení týkající se seedů", + "DownloadClientQbittorrentSettingsUseSslHelpText": "Používat zabezpečené připojení. Viz Možnosti -> WebUI -> Webové uživatelské rozhraní -> ‚Použít HTTPS místo HTTP‘ v qBittorrentu.", + "FilterPlaceHolder": "Hledat indexery", + "FoundCountReleases": "Nalezeno {itemCount} vydání", + "DownloadClientRTorrentSettingsUrlPathHelpText": "Cesta ke koncovému bodu XMLRPC, viz {url}. Při použití ruTorrentu je to obvykle RPC2 nebo [cesta k ruTorrentu]{url2}.", + "DownloadClientSettingsDefaultCategoryHelpText": "Výchozí záložní kategorie, pokud pro vydání neexistuje žádná namapovaná kategorie. Přidáním kategorie specifické pro {appName} se zabrání konfliktům s nesouvisejícími stahováními, která nejsou {appName}. Použití kategorie je nepovinné, ale důrazně se doporučuje.", + "DownloadClientSettingsInitialStateHelpText": "Počáteční stav pro torrenty přidané do {clientName}", + "EditCategory": "Upravit kategorii", + "HistoryDetails": "Podrobnosti o historii", + "Donate": "Darovat", + "DownloadClientSettingsDestinationHelpText": "Ručně určuje cíl stahování, pro použití výchozího nastavení nechte prázdné", + "EnabledRedirected": "Povoleno, Přesměrováno", + "EditSyncProfile": "Upravit profil synchronizace", + "DeleteAppProfile": "Odstranit profil aplikace", + "IndexerSettingsAppsMinimumSeeders": "Minimální počet seederů aplikací", + "UsenetBlackholeNzbFolder": "Složka Nzb", + "SearchIndexers": "Hledat indexery", + "IndexerSettingsAppsMinimumSeedersHelpText": "Minimální počet seederů požadovaných aplikacemi pro indexer, výchozí hodnota synchronizačního profilu je prázdná", + "IndexerProxy": "Proxy indexeru", + "IndexerBeyondHDSettingsRssKeyHelpText": "Klíč RSS ze stránky (Naleznete v Moje zabezpečení => Klíč RSS)", + "IndexerHDBitsSettingsCodecsHelpText": "Pokud není zadáno, použijí se všechny možnosti.", + "IndexerHDBitsSettingsUsernameHelpText": "Uživatelské jméno stránky", + "IndexerAvistazSettingsUsernameHelpTextWarning": "Rozhraní API v tomto indexeru mohou používat pouze hodnosti člen a vyšší.", + "IndexerBeyondHDSettingsApiKeyHelpText": "Klíč API ze stránky (Naleznete v Moje zabezpečení => Klíč API)", + "IndexerBeyondHDSettingsFreeleechOnlyHelpText": "Hledat pouze freeleech vydání", + "IndexerMTeamTpSettingsFreeleechOnlyHelpText": "Hledat pouze freeleech vydání", + "IndexerProxies": "Proxy indexeru", + "IndexerGazelleGamesSettingsFreeleechOnlyHelpText": "Hledat pouze freeleech vydání", + "IndexerHistoryLoadError": "Chyba při načítání historie indexeru", + "IndexerId": "ID indexeru", + "IndexerNoDefinitionCheckHealthCheckMessage": "Indexery nemají žádnou definici a nebudou fungovat: {indexerNames}. Odeberte je a (nebo) znovu přidejte do {appName}.", + "IndexerAlphaRatioSettingsExcludeSceneHelpText": "Vyloučit vydání SCENE z výsledků", + "IndexerAlreadySetup": "Alespoň jedna instance indexeru je již nastavena", + "IndexerAvistazSettingsPasswordHelpText": "Heslo stránky", + "IndexerAvistazSettingsPidHelpText": "PID ze stránky Můj účet nebo Můj profil", + "IndexerAvistazSettingsUsernameHelpText": "Uživatelské jméno stránky", + "IndexerBeyondHDSettingsLimitedOnly": "Pouze omezené", + "IndexerBeyondHDSettingsLimitedOnlyHelpText": "Hledat pouze freeleech (Omezené nahrávání)", + "IndexerCategories": "Kategorie indexeru", + "IndexerDisabled": "Indexer zakázán", + "IndexerDownloadClientHealthCheckMessage": "Indexery s neplatnými klienty pro stahování: {indexerNames}.", + "IndexerDownloadClientHelpText": "Určete, který klient pro stahování se použije pro grabování v rámci {appName} z tohoto indexeru", + "IndexerFailureRate": "Míra selhání indexeru", + "IndexerFileListSettingsFreeleechOnlyHelpText": "Hledat pouze freeleech vydání", + "IndexerFileListSettingsPasskeyHelpText": "Přístupový klíč stránky (Jedná se o alfanumerický řetězec v url adrese trackeru zobrazené v klientovi pro stahování)", + "IndexerGazelleGamesSettingsApiKeyHelpText": "Klíč API ze stránky (Naleznete v Nastavení => Nastavení přístupu)", + "IndexerGazelleGamesSettingsSearchGroupNames": "Hledat názvy skupin", + "IndexerHDBitsSettingsFreeleechOnlyHelpText": "Zobrazit pouze freeleech vydání", + "IndexerHDBitsSettingsOriginsHelpText": "Pokud není zadáno, použijí se všechny možnosti.", + "IndexerHDBitsSettingsUseFilenames": "Použít názvy souborů", + "IndexerHealthCheckNoIndexers": "Nejsou povoleny žádné indexery, {appName} nevrátí výsledky vyhledávání", + "IndexerIPTorrentsSettingsCookieUserAgent": "Uživatelský agent cookie", + "IndexerIPTorrentsSettingsCookieUserAgentHelpText": "Uživatelský agent přidružený cookie použitý z prohlížeče", + "IndexerIPTorrentsSettingsFreeleechOnlyHelpText": "Hledat pouze freeleech vydání", + "IndexerNebulanceSettingsApiKeyHelpText": "Klíč API z Nastavení uživatele > Klíče API. Klíč musí mít oprávnění Seznam a Stáhnout", + "IndexerNewznabSettingsAdditionalParametersHelpText": "Dodatečné parametry Newznab", + "IndexerNewznabSettingsApiKeyHelpText": "Klíč API stránky", + "IndexerObsoleteCheckMessage": "Indexery jsou zastaralé nebo byly aktualizovány: {0}. Odeberte je a (nebo) znovu přidejte do {appName}", + "IndexerOrpheusSettingsApiKeyHelpText": "Klíč API ze stránky (Naleznete v Nastavení => Nastavení přístupu)", + "IndexerPassThePopcornSettingsApiUserHelpText": "Tato nastavení naleznete v nastavení zabezpečení PassThePopcorn (Upravit profil > Zabezpečení).", + "IndexerPassThePopcornSettingsFreeleechOnlyHelpText": "Hledat pouze freeleech vydání", + "IndexerRedactedSettingsApiKeyHelpText": "Klíč API ze stránky (Naleznete v Nastavení => Nastavení přístupu)", + "IndexerRss": "RSS indexeru", + "LastFailure": "Poslední selhání", + "IndexerSettingsAdditionalParameters": "Dodatečné parametry", + "IndexerSettingsApiPath": "Cesta k API", + "IndexerSettingsApiUser": "Uživatel API", + "IndexerAuth": "Ověření indexeru", + "IndexerInfo": "Informace o indexeru", + "IndexerName": "Název indexeru", + "IndexerDetails": "Podrobnosti indexeru", + "IndexerHDBitsSettingsPasskeyHelpText": "Přístupový klíč z Podrobnosti o uživateli", + "IndexerQuery": "Dotaz na indexer", + "IndexerAlphaRatioSettingsExcludeScene": "Vyloučit SCENE", + "IndexerAlphaRatioSettingsFreeleechOnlyHelpText": "Hledat pouze freeleech vydání", + "IndexerBeyondHDSettingsSearchTypes": "Hledat typy", + "IndexerBeyondHDSettingsSearchTypesHelpText": "Vyberte typy vydání, které vás zajímají. Pokud není vybrán žádný, použijí se všechny možnosti.", + "IndexerFileListSettingsUsernameHelpText": "Uživatelské jméno stránky", + "IndexerGazelleGamesSettingsApiKeyHelpTextWarning": "Musí mít oprávnění Uživatel a Torrenty", + "IndexerGazelleGamesSettingsSearchGroupNamesHelpText": "Hledat vydání podle názvů skupin", + "IndexerHDBitsSettingsMediumsHelpText": "Pokud není zadáno, použijí se všechny možnosti.", + "IndexerHDBitsSettingsUseFilenamesHelpText": "Zaškrtněte tuto možnost, pokud chcete používat názvy souborů torrentů jako názvy vydání", + "IndexerNewznabSettingsVipExpirationHelpText": "Zadejte datum (rrrr-mm-dd) pro vypršení VIP nebo prázdné, {appName} bude upozorňovat 1 týden před vypršením VIP", + "IndexerNzbIndexSettingsApiKeyHelpText": "Klíč API stránky", + "IndexerPassThePopcornSettingsApiKeyHelpText": "Klíč API stránky", + "IndexerMTeamTpSettingsApiKeyHelpText": "Klíč API ze stránky (Naleznete v Uživatelský ovládací panel => Zabezpečení => Laboratoř)", + "IndexerPassThePopcornSettingsGoldenPopcornOnly": "Pouze Golden Popcorn", + "IndexerPassThePopcornSettingsGoldenPopcornOnlyHelpText": "Hledat pouze vydání Golden Popcorn", + "IndexerSettingsApiPathHelpText": "Cesta k api, obvykle {url}", + "IndexerAvistazSettingsFreeleechOnlyHelpText": "Hledat pouze freeleech vydání", + "InitialFailure": "Úvodní selhání", + "IndexerTorrentSyndikatSettingsApiKeyHelpText": "Klíč API stránky", + "SearchTypes": "Hledat typy", + "NotificationTriggersHelpText": "Vyber, které události mají vyvolat toto upozornění", + "IndexerSettingsBaseUrl": "Základní URL", + "DownloadClientUTorrentProviderMessage": "uTorrent má historii zahrnování kryptoměnových těžařů, malwaru a reklam, důrazně vám doporučujeme zvolit jiného klienta.", + "IndexerSettingsBaseUrlHelpText": "Vyberte, jakou základní URL bude {appName} používat pro požadavky na web", + "IndexerSettingsPackSeedTimeIndexerHelpText": "Doba, po kterou by měl být balíček (sezóna nebo diskografie) torrentu seedován před zastavením, prázdné pole znamená výchozí nastavení aplikace", + "PackSeedTimeHelpText": "Doba, po kterou by měl být balíček (sezóna nebo diskografie) torrentu seedován před zastavením, prázdné pole znamená výchozí nastavení aplikace" } diff --git a/src/NzbDrone.Core/Localization/Core/da.json b/src/NzbDrone.Core/Localization/Core/da.json index 7868bd720..1a118a8af 100644 --- a/src/NzbDrone.Core/Localization/Core/da.json +++ b/src/NzbDrone.Core/Localization/Core/da.json @@ -8,7 +8,7 @@ "Indexers": "Indexere", "History": "Historie", "HideAdvanced": "Gemt Avancerede", - "HealthNoIssues": "Ingen problemer med din konfiguration", + "NoIssuesWithYourConfiguration": "Ingen problemer med din konfiguration", "Health": "Helbred", "Grabbed": "Grebet", "GeneralSettingsSummary": "Port, SSL, brugernavn/adgangskode, proxy, analyse og opdateringer", @@ -33,7 +33,7 @@ "CustomFilters": "Bruger Tilpassede Filtere", "ConnectSettingsSummary": "Notifikationer, forbindelser til medieservere/-afspillere og brugerdefinerede scripts", "Connections": "Forbindelser", - "ConnectionLost": "Forbindelse Mistet", + "ConnectionLost": "Forbindelse mistet", "Connect": "Notifikationer", "Component": "Komponent", "Columns": "Kolonner", @@ -57,7 +57,7 @@ "MIA": "MIA", "ResetAPIKey": "Nulstil API-nøgle", "SettingsTimeFormat": "Tidsformat", - "SystemTimeCheckMessage": "Systemtiden er slukket mere end 1 dag. Planlagte opgaver kører muligvis ikke korrekt, før tiden er rettet", + "SystemTimeHealthCheckMessage": "Systemtiden er slukket mere end 1 dag. Planlagte opgaver kører muligvis ikke korrekt, før tiden er rettet", "UnsavedChanges": "Ugemte ændringer", "Updates": "Opdateringer", "MoreInfo": "Mere info", @@ -101,12 +101,12 @@ "CloseCurrentModal": "Luk Nuværende Modal", "CouldNotConnectSignalR": "Kunne ikke oprette forbindelse til SignalR, UI opdateres ikke", "DeleteApplicationMessageText": "Er du sikker på, at du vil slette underretningen '{0}'?", - "DeleteDownloadClientMessageText": "Er du sikker på, at du vil slette downloadklienten '{0}'?", + "DeleteDownloadClientMessageText": "Er du sikker på, at du vil fjerne downloadklienten »{name}«?", "DeleteIndexerProxyMessageText": "Er du sikker på, at du vil slette tagget '{0}'?", "DeleteNotification": "Slet underretning", - "DeleteNotificationMessageText": "Er du sikker på, at du vil slette underretningen '{0}'?", + "DeleteNotificationMessageText": "Er du sikker på, at du vil slette notifikationen »{name}«?", "DeleteTag": "Slet tag", - "DeleteTagMessageText": "Er du sikker på, at du vil slette tagget '{0}'?", + "DeleteTagMessageText": "Er du sikker på, at du vil slette etiketten »{label}«?", "Discord": "Discord", "Docker": "Docker", "Donations": "Donationer", @@ -232,7 +232,7 @@ "UnableToAddANewAppProfilePleaseTryAgain": "Kan ikke tilføje en ny kvalitetsprofil, prøv igen.", "UnableToAddANewDownloadClientPleaseTryAgain": "Kunne ikke tilføje en ny downloadklient. Prøv igen.", "UnableToAddANewIndexerPleaseTryAgain": "Kunne ikke tilføje en ny indekser. Prøv igen.", - "UnableToLoadBackups": "Kunne ikke indlæse sikkerhedskopier", + "BackupsLoadError": "Kunne ikke indlæse sikkerhedskopier", "UnableToLoadGeneralSettings": "Kan ikke indlæse generelle indstillinger", "UnableToLoadNotifications": "Kunne ikke indlæse meddelelser", "UnableToLoadTags": "Kan ikke indlæse tags", @@ -266,7 +266,7 @@ "Status": "Status", "DownloadClientsLoadError": "Kunne ikke indlæse downloadklienter", "UpdateStartupTranslocationHealthCheckMessage": "Kan ikke installere opdatering, fordi startmappen '{startupFolder}' er i en App Translocation-mappe.", - "UpdateMechanismHelpText": "Brug den indbyggede opdateringsfunktion eller et script", + "UpdateMechanismHelpText": "Brug {appName}s indbyggede opdateringsfunktion eller et script", "View": "Udsigt", "Warn": "Advare", "AddingTag": "Tilføjer tag", @@ -302,12 +302,12 @@ "InteractiveSearch": "Interaktiv søgning", "LogFiles": "Logfiler", "ApiKey": "API-nøgle", - "AppDataDirectory": "AppData-bibliotek", + "AppDataDirectory": "AppData-mappe", "CertificateValidationHelpText": "Skift, hvor streng HTTPS-certificering er", "ChangeHasNotBeenSavedYet": "Ændring er endnu ikke gemt", "ConnectSettings": "Forbind indstillinger", "DeleteBackup": "Slet sikkerhedskopi", - "DeleteBackupMessageText": "Er du sikker på, at du vil slette sikkerhedskopien '{0}'?", + "DeleteBackupMessageText": "Er du sikker på, at du vil slette sikkerhedskopien »{name}«?", "DeleteDownloadClient": "Slet Download Client", "MaintenanceRelease": "Vedligeholdelsesfrigivelse: fejlrettelser og andre forbedringer. Se Github Commit History for flere detaljer", "Filters": "Filter", @@ -340,7 +340,7 @@ "Notification": "Notifikationer", "Remove": "Fjerne", "Replace": "erstat", - "TheLatestVersionIsAlreadyInstalled": "Den seneste version af {appName} er allerede installeret", + "OnLatestVersion": "Den seneste version af {appName} er allerede installeret", "Year": "År", "ApplyTagsHelpTextAdd": "Tilføj: Føj tags til den eksisterende liste over tags", "ApplyTagsHelpTextHowToApplyApplications": "Sådan anvendes tags på de valgte film", @@ -360,7 +360,7 @@ "DeleteAppProfileMessageText": "Er du sikker på, at du vil slette kvalitetsprofilen {0}", "RecentChanges": "Seneste ændringer", "WhatsNew": "Hvad er nyt?", - "ConnectionLostReconnect": "Radarr vil prøve at tilslutte automatisk, eller du kan klikke genindlæs forneden.", + "ConnectionLostReconnect": "{appName} vil prøve at tilslutte automatisk. Ellers du kan klikke genindlæs forneden.", "minutes": "Protokoller", "NotificationStatusAllClientHealthCheckMessage": "Alle lister er utilgængelige på grund af fejl", "NotificationStatusSingleClientHealthCheckMessage": "Lister utilgængelige på grund af fejl: {notificationNames}", @@ -375,7 +375,7 @@ "AddConnection": "Tilføj forbindelse", "EditConnectionImplementation": "Tilføj forbindelse - {implementationName}", "AddApplicationImplementation": "Tilføj forbindelse - {implementationName}", - "AddIndexerImplementation": "Tilføj betingelse - {implementationName}", + "AddIndexerImplementation": "Tilføj indeksør - {implementationName}", "ApplyChanges": "Anvend ændringer", "AddDownloadClientImplementation": "Tilføj downloadklient - {implementationName}", "Album": "album", @@ -392,5 +392,41 @@ "EditApplicationImplementation": "Tilføj forbindelse - {implementationName}", "EditDownloadClientImplementation": "Tilføj downloadklient - {implementationName}", "IndexerHDBitsSettingsMediums": "Medium", - "CustomFilter": "Bruger Tilpassede Filtere" + "CustomFilter": "Bruger Tilpassede Filtere", + "ProxyValidationBadRequest": "Kunne ikke teste proxy. Statuskode: {statusCode}", + "GrabRelease": "Hent udgivelse", + "Script": "Manuskript", + "BuiltIn": "Indbygget", + "PublishedDate": "Udgivelsesdato", + "AllSearchResultsHiddenByFilter": "Alle resultater skjules af det anvendte filter", + "AddApplication": "Tilføj Applikation", + "AddCategory": "Tilføj Kategori", + "ActiveApps": "Aktive Apps", + "NoEventsFound": "Ingen begivenheder fundet", + "AptUpdater": "Brug apt til at installere opdateringen", + "DockerUpdater": "opdater docker-containeren for at modtage opdateringen", + "Download": "Hent", + "ErrorRestoringBackup": "Fejl ved gendannelse af sikkerhedskopi", + "ExternalUpdater": "{appName} er konfigureret til at bruge en ekstern opdateringsmekanisme", + "RestartReloadNote": "Bemærk: {appName} genstarter automatisk og genindlæser brugergrænsefladen under gendannelsesprocessen.", + "UpdateAppDirectlyLoadError": "Kan ikke opdatere {appName} direkte,", + "InstallLatest": "Installer senest", + "Clone": "Luk", + "CurrentlyInstalled": "Aktuelt installeret", + "Stats": "Status", + "Mixed": "Fast", + "PrioritySettings": "Prioritet: {priority}", + "WouldYouLikeToRestoreBackup": "Vil du gendanne sikkerhedskopien »{name}«?", + "AppProfileSelectHelpText": "App-profiler bruges til at styre indstillinger for RSS, automatisk søgning og interaktiv søgning ved synkronisering med applikationer", + "ActiveIndexers": "Aktive indeksører", + "TheLogLevelDefault": "Logniveauet er som standard 'Info' og kan ændres under [Generelle indstillinger](/settings/general)", + "AddedToDownloadClient": "Udgivelse føjet til klient", + "AdvancedSettingsHiddenClickToShow": "Avancerede indstillinger er skjult. Klik for at vise", + "AdvancedSettingsShownClickToHide": "Avancerede indstillinger vises. Klik for at skjule", + "ApiKeyValidationHealthCheckMessage": "Opdater din API-nøgle til at være på mindste {length} karakterer. Dette kan gøres i indstillingerne eller i konfigurationsfilen", + "AppProfileInUse": "App-profil i brug", + "Episode": "afsnit", + "Artist": "kunstner", + "Id": "ID", + "Encoding": "Indkodning" } diff --git a/src/NzbDrone.Core/Localization/Core/de.json b/src/NzbDrone.Core/Localization/Core/de.json index 52ef727f6..b07ede40b 100644 --- a/src/NzbDrone.Core/Localization/Core/de.json +++ b/src/NzbDrone.Core/Localization/Core/de.json @@ -87,13 +87,13 @@ "Delete": "Löschen", "DeleteAppProfile": "App-Profil löschen", "DeleteApplication": "Applikation löschen", - "DeleteApplicationMessageText": "Wirklich die Applikation '{0}' löschen?", + "DeleteApplicationMessageText": "Bist du sicher, dass du die Anwendung „{name}“ löschen möchtest?", "DeleteBackup": "Sicherung löschen", "DeleteBackupMessageText": "Soll das Backup '{name}' wirklich gelöscht werden?", "DeleteDownloadClient": "Download-Client löschen", "DeleteDownloadClientMessageText": "Bist du sicher, dass du den Download Client '{name}' wirklich löschen willst?", "DeleteIndexerProxy": "Indexer Proxy löschen", - "DeleteIndexerProxyMessageText": "Tag '{0}' wirklich löschen?", + "DeleteIndexerProxyMessageText": "Bist du sicher, dass du den Indexer-Proxy „{name}“ löschen möchtest?", "DeleteNotification": "Benachrichtigung löschen", "DeleteNotificationMessageText": "Bist du sicher, dass du die Benachrichtigung '{name}' wirklich löschen willst?", "DeleteTag": "Tag löschen", @@ -105,11 +105,11 @@ "Discord": "Discord", "Docker": "Docker", "Donations": "Spenden", - "DownloadClient": "Download Client", + "DownloadClient": "Downloader", "DownloadClientSettings": "Downloader Einstellungen", "DownloadClientStatusAllClientHealthCheckMessage": "Alle Download Clients sind aufgrund von Fehlern nicht verfügbar", "DownloadClientStatusSingleClientHealthCheckMessage": "Download Clients aufgrund von Fehlern nicht verfügbar: {downloadClientNames}", - "DownloadClients": "Downloader", + "DownloadClients": "Download Clients", "DownloadClientsSettingsSummary": "Download der Client-Konfigurationen für die Integration in die {appName} UI-Suche", "Duration": "Dauer", "Edit": "Bearbeiten", @@ -118,10 +118,10 @@ "ElapsedTime": "Vergangene Zeit", "Enable": "Aktivieren", "EnableAutomaticSearch": "Automatische Suche einschalten", - "EnableAutomaticSearchHelpText": "Wird für automatische Suchen genutzt die vom Benutzer oder von {appName} gestartet werden", + "EnableAutomaticSearchHelpText": "Wird verwendet, wenn die automatische Suche über die Benutzeroberfläche oder durch {appName} durchgeführt wird.", "EnableIndexer": "Indexer aktivieren", "EnableInteractiveSearch": "Interaktive Suche einschalten", - "EnableInteractiveSearchHelpText": "Wird bei der manuellen Suche benutzt", + "EnableInteractiveSearchHelpText": "Wird verwendet, wenn die interaktive Suche verwendet wird", "EnableRss": "RSS aktivieren", "EnableRssHelpText": "RSS-Feed für Indexer aktivieren", "EnableSSL": "SSL", @@ -131,13 +131,13 @@ "Encoding": "Codierung", "Ended": "Beendet", "Error": "Fehler", - "ErrorLoadingContents": "Fehler beim laden der Inhalte", - "EventType": "Event Typ", - "Events": "Events", + "ErrorLoadingContents": "Fehler beim Laden von Inhalten", + "EventType": "Ereignistyp", + "Events": "Ereignisse", "Exception": "Ausnahme", "ExistingTag": "Vorhandener Tag", "Failed": "Fehlgeschlagen", - "FeatureRequests": "Funktionsanfragen", + "FeatureRequests": "Feature Anfragen", "Filename": "Dateiname", "Files": "Dateien", "Filter": "Filter", @@ -153,19 +153,19 @@ "GeneralSettingsSummary": "Port, SSL, Benutzername/Passwort, Proxy, Analytik und Updates", "GrabReleases": "Release erfassen", "GrabTitle": "Titel holen", - "Grabbed": "Erfasste", + "Grabbed": "Geholt", "Grabs": "Erfasse", - "Health": "Zustandsüberwachung", - "HealthNoIssues": "Keine Probleme mit deiner Konfiguration", - "HideAdvanced": "Erweiterte Ansicht", + "Health": "Gesundheit", + "NoIssuesWithYourConfiguration": "Keine Probleme mit deiner Konfiguration", + "HideAdvanced": "Erweiterte Einstellungen ausblenden", "History": "Verlauf", "HistoryCleanup": "Verlaufsbereinigung", "HistoryCleanupDaysHelpText": "Auf 0 setzen um das automatische leeren des Papierkorbs zu deaktivieren", "HistoryCleanupDaysHelpTextWarning": "Datien im Papierkorb die älter sind als der gewählte Wert, werden endgültig gelöscht", - "HomePage": "Startseite", + "HomePage": "Hauptseite", "Host": "Host", "Hostname": "Hostname", - "Id": "Id", + "Id": "ID", "IgnoredAddresses": "Ignorierte Adressen", "IllRestartLater": "Später neustarten", "IncludeHealthWarningsHelpText": "Zustandswarnung", @@ -176,12 +176,12 @@ "IndexerFlags": "Indexer-Flags", "IndexerHealthCheckNoIndexers": "Keine Indexer aktiviert, {appName} wird keine Suchergebnisse zurückgeben", "IndexerInfo": "Indexer-Info", - "IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Alle Indexer sind wegen über 6 Stunden langen bestehender Fehler nicht verfügbar", - "IndexerLongTermStatusUnavailableHealthCheckMessage": "Indexer wegen über 6 Stunden langen bestehenden Fehlern nicht verfügbar: {indexerNames}", + "IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Alle Indexer sind aufgrund von Fehlern länger als 6 Stunden nicht verfügbar", + "IndexerLongTermStatusUnavailableHealthCheckMessage": "Indexer sind aufgrund von Fehlern länger als 6 Stunden nicht verfügbar: {indexerNames}", "IndexerName": "Indexer-Name", - "IndexerNoDefCheckMessage": "Indexer haben keine Definition und werden nicht funktionieren: {0}. Bitte entferne und (oder) füge diese neu zu {appName} hinzu", + "IndexerNoDefinitionCheckHealthCheckMessage": "Indexer haben keine Definition und werden nicht funktionieren: {indexerNames}. Bitte entferne und (oder) füge diese neu zu {appName} hinzu", "IndexerObsoleteCheckMessage": "Indexer sind nicht mehr verfügbar oder wurden aktualiiert: {0}. Bitte enfernen und (oder) neu zu {appName} hinzufügen", - "IndexerPriority": "Priorität", + "IndexerPriority": "Indexer-Priorität", "IndexerPriorityHelpText": "Indexer Priorität von 1 (höchste) bis 50 (niedrigste). Standard: 25.", "IndexerProxies": "Indexer-Proxies", "IndexerProxy": "Indexer-Proxy", @@ -192,27 +192,27 @@ "IndexerSettingsSummary": "Konfiguration verschiedener globaler Indexer Einstellungen, einschließlich Proxies.", "IndexerSite": "Indexer-Seite", "IndexerStatusAllUnavailableHealthCheckMessage": "Alle Indexer sind aufgrund von Fehlern nicht verfügbar", - "IndexerStatusUnavailableHealthCheckMessage": "Indexer aufgrund von Fehlern nicht verfügbar: {indexerNames}", - "IndexerTagsHelpText": "Benutze Tags, um Indexer-Proxies zu spezifizieren, mit welchen Apps der Indexer synchronisiert oder um Indexer zu organisieren.", + "IndexerStatusUnavailableHealthCheckMessage": "Indexer nicht verfügbar aufgrund von Fehlern: {indexerNames}", + "IndexerTagsHelpText": "Verwende Tags, um Indexer-Proxys oder die Apps, mit denen der Indexer synchronisiert wird, anzugeben.", "IndexerVipExpiredHealthCheckMessage": "Die VIP Indexer Vorteile sind abgelaufen: {indexerNames}", "IndexerVipExpiringHealthCheckMessage": "Die Indexer VIP Vorteile verfallen bald: {indexerNames}", "Indexers": "Indexer", "Info": "Info", "InstanceName": "Instanzname", - "InstanceNameHelpText": "Instanzname im Browser-Tab und für Syslog-Anwendungsname", + "InstanceNameHelpText": "Instanzname im Tab und für den Syslog-App-Namen", "InteractiveSearch": "Interaktive Suche", "Interval": "Intervall", "KeyboardShortcuts": "Tastenkürzel", "Language": "Sprache", "LastDuration": "Letzte Dauer", "LastExecution": "Letzte Ausführung", - "LastWriteTime": "Zuletzt beschrieben", + "LastWriteTime": "Letzte Schreibzeit", "LaunchBrowserHelpText": " Öffne die Startseite von {appName} im Webbrowser nach dem Start.", - "Level": "Stufe", + "Level": "Level", "Link": "Links", - "LogFiles": "Protokolle", - "LogLevel": "Log Level", - "LogLevelTraceHelpTextWarning": "Trace logging sollte nur kurzzeitig aktiviert werden", + "LogFiles": "Protokolldateien", + "LogLevel": "Protokollstufe", + "LogLevelTraceHelpTextWarning": "Die Trace-Protokollierung sollte nur vorübergehend aktiviert werden", "Logging": "Protokollierung", "Logs": "Protokolle", "MIA": "MIA", @@ -220,7 +220,7 @@ "Manual": "Manuell", "MappedDrivesRunningAsService": "Zugeordnete Netzlaufwerke sind nicht verfügbar, wenn {appName} als Windows-Dienst ausgeführt wird. Bitte lesen Sie die FAQ für weitere Informationen", "MassEditor": "Masseneditor", - "Mechanism": "Verfahren", + "Mechanism": "Mechanismus", "Message": "Nachricht", "MinimumSeeders": "Mindest-Seeder", "MinimumSeedersHelpText": "Minimale Anzahl an Seedern die von der Anwendung benötigt werden um den Indexer zu holen", @@ -236,47 +236,47 @@ "New": "Neu", "NextExecution": "Nächste Ausführung", "No": "Nein", - "NoBackupsAreAvailable": "Es sind keine Backups vorhanden", + "NoBackupsAreAvailable": "Keine Sicherungen verfügbar", "NoChange": "Keine Änderung", "NoChanges": "Keine Änderungen", - "NoLeaveIt": "Nein, nicht ändern", + "NoLeaveIt": "Nein, lass es", "NoLinks": "Keine Links", - "NoLogFiles": "Keine Log-Dateien", + "NoLogFiles": "Keine Logdateien", "NoSearchResultsFound": "Keine Suchergebnisse gefunden. Versuchen Sie unten eine erneute Suche durchzuführen.", - "NoTagsHaveBeenAddedYet": "Es wurden noch keine Tags erstellt", + "NoTagsHaveBeenAddedYet": "Es wurden noch keine Tags hinzugefügt", "NoUpdatesAreAvailable": "Es sind keine Updates verfügbar", "NotSupported": "Nicht unterstützt", "Notification": "Benachrichtigungen", - "NotificationTriggers": "Benachrichtigungs Auslöser", - "NotificationTriggersHelpText": "Auslöser für diese Benachrichtigung auswählen", + "NotificationTriggers": "Benachrichtigungs-Auslöser", + "NotificationTriggersHelpText": "Wähle aus, welche Ereignisse diese Benachrichtigung auslösen sollen", "Notifications": "Benachrichtigungen", "OAuthPopupMessage": "Dein Browser blockiert Pop-ups", - "Ok": "OK", + "Ok": "Ok", "OnApplicationUpdate": "Bei Anwendungsaktualisierung", "OnApplicationUpdateHelpText": "Bei Anwendungsaktualisierung", - "OnGrab": "Bei Erfassung", - "OnHealthIssue": "Bei Zustandsproblem", + "OnGrab": "Bei Release-Grabs", + "OnHealthIssue": "Bei Gesundheitsproblem", "OnHealthIssueHelpText": "Zustandsproblem", "OpenBrowserOnStart": "Browser beim Start öffnen", "OpenThisModal": "Dieses Modal öffnen", "Options": "Optionen", - "PackageVersion": "Paket Version", + "PackageVersion": "Paketversion", "PageSize": "Einträge pro Seite", "PageSizeHelpText": "Anzahl der Einträge pro Seite", "Parameters": "Parameter", "Password": "Passwort", "Peers": "Peers", - "PendingChangesDiscardChanges": "Änderungen verwerfen und schließen", - "PendingChangesMessage": "Es gibt noch ungespeicherte Änderungen, bist du sicher, dass du die Seite verlassen möchtest?", - "PendingChangesStayReview": "Auf der Seite bleiben", + "PendingChangesDiscardChanges": "Änderungen verwerfen und verlassen", + "PendingChangesMessage": "Du hast ungespeicherte Änderungen, bist du sicher, dass du diese Seite verlassen möchtest?", + "PendingChangesStayReview": "Bleiben und Änderungen überprüfen", "Port": "Port", - "PortNumber": "Port Nummer", + "PortNumber": "Portnummer", "Presets": "Voreinstellungen", "Priority": "Priorität", "Privacy": "Privatsphäre", "Private": "Privat", "Protocol": "Protokoll", - "ProwlarrSupportsAnyDownloadClient": "Jeder Downloader der den Newznab-Standard verwendet oder unten aufgelistet ist wird untertützt.", + "ProwlarrSupportsAnyDownloadClient": "{appName} unterstützt jeden der unten aufgeführten Download-Clients.", "ProwlarrSupportsAnyIndexer": "{appName} unterstützt alle Indexer, welcher den Newznab/Torznab Standard implementiert (verwende 'Generic Newznab' (für Usenet) oder 'Generic Torznab' (für Torrents)) und darüber hinaus viele weitere Indexer. Wählen Sie im Folgenden Ihren Indexer aus der Liste.", "Proxies": "Proxies", "Proxy": "Proxy", @@ -294,7 +294,7 @@ "Queue": "Warteschlange", "Queued": "In Warteschlange", "Rss": "RSS", - "RssIsNotSupportedWithThisIndexer": "RSS wird von diesem Indexer nicht unterstützt", + "RssIsNotSupportedWithThisIndexer": "RSS wird mit diesem Indexer nicht unterstützt", "RawSearchSupported": "Raw-Suche unterstützt", "ReadTheWikiForMoreInformation": "Lesen Sie das Wiki für weitere Informationen", "Reddit": "Reddit", @@ -304,7 +304,7 @@ "RefreshMovie": "Film aktualisieren", "ReleaseBranchCheckOfficialBranchMessage": "Zweig {0} ist kein gültiger {appName}-Release-Zweig. Sie erhalten keine Updates", "ReleaseStatus": "Releasestatus", - "Reload": "Neuladen", + "Reload": "Neu laden", "Remove": "Entfernen", "RemoveFilter": "Filter entfernen", "RemovedFromTaskQueue": "Aus der Aufgabenwarteschlange entfernt", @@ -319,7 +319,7 @@ "Restore": "Wiederherstellen", "RestoreBackup": "Sicherung wiederherstellen", "Result": "Ergebnis", - "Retention": "Aufbewahrung ( Retention )", + "Retention": "Aufbewahrung", "SSLCertPassword": "SSL Zertifikat Passwort", "SSLCertPasswordHelpText": "Passwort für die PFX Datei", "SSLCertPath": "Pfad zum SSL Zertifikat", @@ -329,8 +329,8 @@ "SaveChanges": "Änderungen speichern", "SaveSettings": "Einstellungen speichern", "Scheduled": "Geplant", - "ScriptPath": "Script Pfad", - "Search": "Suche", + "ScriptPath": "Skript-Pfad", + "Search": "Suchen", "SearchCapabilities": "Suchfähigkeiten", "SearchIndexers": "Indexer suchen", "SearchType": "Suchtyp", @@ -360,7 +360,7 @@ "SettingsTimeFormat": "Zeitformat", "ShowAdvanced": "Erweitert anzeigen", "ShowSearch": "Suche anzeigen", - "ShowSearchHelpText": "Suchbutton anzeigen beim draufzeigen", + "ShowSearchHelpText": "Suchschaltfläche beim Überfahren anzeigen", "Shutdown": "Herunterfahren", "Size": "Größe", "Sort": "Sortieren", @@ -379,12 +379,12 @@ "SyncProfile": "Sync-Profile", "SyncProfiles": "Sync-Profile", "System": "System", - "SystemTimeCheckMessage": "Die Systemzeit ist um einen Tag versetzt. Bis die Zeit korrigiert wurde, könnten die geplanten Aufgaben nicht korrekt ausgeführt werden", + "SystemTimeHealthCheckMessage": "Die Systemzeit ist um einen Tag versetzt. Bis die Zeit korrigiert wurde, könnten die geplanten Aufgaben nicht korrekt ausgeführt werden", "TVSearchTypes": "Suchtyp", - "TableOptions": "Tabellen Optionen", + "TableOptions": "Tabellenoptionen", "TableOptionsColumnsMessage": "Wähle aus welche Spalten angezeigt werden und in welcher Reihenfolge", "TagCannotBeDeletedWhileInUse": "Kann während der Benutzung nicht gelöscht werden", - "TagIsNotUsedAndCanBeDeleted": "Tag wird nicht benutzt und kann gelöscht werden", + "TagIsNotUsedAndCanBeDeleted": "Tag wird nicht verwendet und kann gelöscht werden", "Tags": "Tags", "TagsHelpText": "Wird auf Filme mit mindestens einem passenden Tag angewandt", "TagsSettingsSummary": "Sehen Sie sich alle Tags und deren Verwendung an. Nicht verwendete Tags können entfernt werden", @@ -394,8 +394,8 @@ "TestAllApps": "Alle Apps testen", "TestAllClients": "Prüfe alle Clients", "TestAllIndexers": "Prüfe alle Indexer", - "TheLatestVersionIsAlreadyInstalled": "Die aktuellste Version ist bereits installiert", - "ThemeHelpText": "Ändere das UI-Theme der Anwendung. Das 'Auto'-Theme verwendet dein Betriebssystem-Theme, um den hellen oder dunklen Modus einzustellen. Inspiriert von {0}", + "OnLatestVersion": "Die neueste Version von {appName} ist bereits installiert", + "ThemeHelpText": "Ändere das UI-Design der Anwendung, das 'Auto'-Design verwendet das Betriebssystem-Design, um den Hell- oder Dunkelmodus festzulegen. Inspiriert von {inspiredBy}.", "Time": "Zeit", "Title": "Titel", "Today": "Heute", @@ -406,7 +406,7 @@ "Type": "Typ", "UI": "Oberfläche", "UILanguage": "Oberflächen Sprache ( UI Language )", - "UILanguageHelpText": "Sprache für die gesamte Oberfläche", + "UILanguageHelpText": "Sprache, die {appName} für die Benutzeroberfläche verwenden wird", "UILanguageHelpTextWarning": "Webseite muss neu geladen werden", "UISettings": "Benutzeroberflächen Einstellungen", "UISettingsSummary": "Optionen für Datum, Sprache und Farbbeinträchtigungen", @@ -419,9 +419,9 @@ "UnableToAddANewNotificationPleaseTryAgain": "Die neue Benachrichtigung konnte nicht hinzugefügt werden, bitte erneut probieren.", "UnableToLoadAppProfiles": "App-Profile können nicht geladen werden", "ApplicationsLoadError": "Anwendungsliste kann nicht geladen werden", - "UnableToLoadBackups": "Sicherungen können nicht geladen werden", + "BackupsLoadError": "Sicherungen können nicht geladen werden", "UnableToLoadDevelopmentSettings": "Entwicklereinstellungen konnten nicht geladen werden", - "DownloadClientsLoadError": "Downloader konnten nicht geladen werden", + "DownloadClientsLoadError": "Download Clients können nicht geladen werden", "UnableToLoadGeneralSettings": "Allgemeine Einstellungen konnten nicht geladen werden", "UnableToLoadHistory": "Verlauf konnte nicht geladen werden", "UnableToLoadIndexerProxies": "Indexer-Proxies können nicht geladen werden", @@ -454,14 +454,14 @@ "YesCancel": "Ja Abbrechen", "Yesterday": "Gestern", "OnHealthRestoredHelpText": "Bei Wiederherstellung des Zustands", - "OnHealthRestored": "Bei Wiederherstellung des Zustands", + "OnHealthRestored": "Bei Wiederherstellung der Gesundheit", "StopSelecting": "Auswahl stoppen", "ApplicationURL": "Anwendungs-URL", "ApplicationUrlHelpText": "Die externe URL dieser Anwendung, einschließlich http(s)://, Port und URL-Basis", "ApplyChanges": "Änderungen anwenden", "CountIndexersSelected": "{count} Indexer ausgewählt", "DeleteSelectedDownloadClients": "Lösche Download Client(s)", - "DeleteSelectedApplicationsMessageText": "Indexer '{0}' wirklich löschen?", + "DeleteSelectedApplicationsMessageText": "Bist du sicher, dass du {count} ausgewählte Anwendung(en) löschen möchtest?", "DeleteSelectedDownloadClientsMessageText": "Sind Sie sicher, dass Sie {count} ausgewählte Download-Clients löschen möchten?", "DeleteSelectedIndexersMessageText": "Sind Sie sicher, dass Sie {count} ausgewählte(n) Indexer löschen möchten?", "EditSelectedDownloadClients": "Ausgewählte Download Clienten bearbeiten", @@ -485,7 +485,7 @@ "More": "Mehr", "Publisher": "Herausgeber", "Track": "Trace", - "UpdateAvailableHealthCheckMessage": "Neue Version verfügbar", + "UpdateAvailableHealthCheckMessage": "Ein neues Update ist verfügbar: {version}", "Year": "Jahr", "Album": "Album", "Artist": "Interpret", @@ -498,8 +498,8 @@ "minutes": "Minuten", "DeleteAppProfileMessageText": "Qualitätsprofil '{0}' wirklich löschen?", "AddConnection": "Verbindung hinzufügen", - "NotificationStatusAllClientHealthCheckMessage": "Wegen Fehlern sind keine Applikationen verfügbar", - "NotificationStatusSingleClientHealthCheckMessage": "Applikationen wegen folgender Fehler nicht verfügbar: {notificationNames}", + "NotificationStatusAllClientHealthCheckMessage": "Alle Benachrichtigungen sind aufgrund von Fehlern nicht verfügbar", + "NotificationStatusSingleClientHealthCheckMessage": "Benachrichtigungen nicht verfügbar wegen Fehlern: {notificationNames}", "AuthBasic": "Basis (Browser-Popup)", "AuthForm": "Formulare (Anmeldeseite)", "DisabledForLocalAddresses": "Für lokale Adressen deaktiviert", @@ -518,7 +518,7 @@ "ActiveApps": "Aktive Apps", "ActiveIndexers": "Aktive Indexer", "AppsMinimumSeeders": "Apps Mindestanzahl von Seedern", - "ApplicationTagsHelpText": "Synchronisiere Indexer für diese Anwendung die keine passenden Tags oder mindestens 1 passendes Tag haben", + "ApplicationTagsHelpText": "Indexer mit dieser Anwendung synchronisieren, die mindestens einen übereinstimmenden Tag haben. Wenn hier keine Tags aufgeführt sind, wird kein Indexer aufgrund seiner Tags von der Synchronisierung ausgeschlossen.", "ApplicationTagsHelpTextWarning": "Tags sollten mit Vorsicht verwendet werden, da sie ungewollte Effekte haben können. Eine Anwendung mit einem Tag synchronisiert nur Indexer die den Gleichen Tag haben.", "AddApplicationImplementation": "Anwendung hinzufügen - {implementationName}", "AddConnectionImplementation": "Verbindung hinzufügen - {implementationName}", @@ -529,22 +529,282 @@ "AuthenticationRequiredWarning": "Um unberechtigte Fernzugriffe zu vermeiden benötigt {appName} jetzt , dass Authentifizierung eingeschaltet ist. Du kannst Authentifizierung optional für lokale Adressen ausschalten.", "AuthenticationRequired": "Authentifizierung benötigt", "AuthenticationRequiredHelpText": "Ändern, welche anfragen Authentifizierung benötigen. Ändere nichts wenn du dir nicht des Risikos bewusst bist.", - "AuthenticationRequiredUsernameHelpTextWarning": "Gib einen neuen Benutzernamen ein", + "AuthenticationRequiredUsernameHelpTextWarning": "Neuen Benutzernamen eingeben", "AuthenticationMethodHelpTextWarning": "Bitte wähle eine gültige Authentifizierungsmethode aus", - "AuthenticationRequiredPasswordHelpTextWarning": "Gib ein neues Passwort ein", + "AuthenticationRequiredPasswordHelpTextWarning": "Neues Passwort eingeben", "AuthenticationRequiredPasswordConfirmationHelpTextWarning": "Neues Passwort bestätigen", "DefaultNameCopiedProfile": "{name} – Kopieren", "AuthenticationMethod": "Authentifizierungsmethode", "Clone": "Klonen", "CountDownloadClientsSelected": "{count} Download-Client(s) ausgewählt", - "EditConnectionImplementation": "Verbindung hinzufügen - {implementationName}", - "EditDownloadClientImplementation": "Download-Client hinzufügen - {implementationName}", + "EditConnectionImplementation": "Verbindung bearbeiten - {implementationName}", + "EditDownloadClientImplementation": "Download Client bearbeiten - {implementationName}", "IndexerTagsHelpTextWarning": "Tags sollten mit Vorsicht verwendet werden, da sie ungewollte Effekte haben können. Eine Anwendung mit einem Tag synchronisiert nur Indexer die den Gleichen Tag haben.", - "EditIndexerImplementation": "Indexer hinzufügen - {implementationName}", + "EditIndexerImplementation": "Indexer bearbeiten - {implementationName}", "EditApplicationImplementation": "Anwendung hinzufügen - {implementationName}", "EditIndexerProxyImplementation": "Indexer Proxy hinzufügen - {implementationName}", "CountApplicationsSelected": "{count} Ausgewählte Sammlung(en)", "DownloadClientAriaSettingsDirectoryHelpText": "Optionaler Speicherort für Downloads. Lassen Sie das Feld leer, um den standardmäßigen rTorrent-Speicherort zu verwenden", "ManageClients": "Verwalte Clienten", - "BlackholeFolderHelpText": "Ordner, in dem {appName} die Datei {extension} speichert" + "BlackholeFolderHelpText": "Ordner, in dem {appName} die Datei {extension} speichert", + "DownloadClientSettingsInitialStateHelpText": "Anfangszustand für zu {clientName} hinzugefügte Torrents", + "DownloadClientSettingsDestinationHelpText": "Legt das Ziel für den Download manuell fest; lassen Sie es leer, um die Standardeinstellung zu verwenden", + "DownloadClientSettingsUrlBaseHelpText": "Fügt ein Präfix zur {clientName} Url hinzu, z.B. {url}", + "DownloadClientSettingsUseSslHelpText": "Sichere Verbindung verwenden, wenn Verbindung zu {clientName} hergestellt wird", + "DownloadClientTransmissionSettingsDirectoryHelpText": "Optionaler Speicherort für Downloads; leer lassen, um den Standardspeicherort für Übertragungen zu verwenden", + "CustomFilter": "Benutzerdefinierter Filter", + "DownloadClientSettingsInitialState": "Ausgangszustand", + "Donate": "Spenden", + "DownloadClientFreeboxSettingsAppId": "App-ID", + "DownloadClientFreeboxSettingsAppIdHelpText": "App-ID, die beim Erstellen des Zugriffs auf die Freebox-API angegeben wird (z. B. „app_id“)", + "DownloadClientFreeboxSettingsPortHelpText": "Port, der für den Zugriff auf die Freebox-Schnittstelle verwendet wird, standardmäßig ist „{port}“", + "DownloadClientNzbgetSettingsAddPausedHelpText": "Diese Option erfordert mindestens NzbGet Version 16.0", + "DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText": "Laden Sie zuerst das erste und das letzte Stück herunter (qBittorrent 4.1.0+)", + "IndexerBeyondHDSettingsSearchTypes": "Suchtyp", + "DownloadClientFloodSettingsUrlBaseHelpText": "Fügt der Flood-API ein Präfix hinzu, z. B. {url}", + "DownloadClientFreeboxSettingsApiUrl": "API-URL", + "DownloadClientFreeboxSettingsApiUrlHelpText": "Definiere die Freebox-API-Basis-URL mit der API-Version, z. B. '{url}', standardmäßig '{defaultApiUrl}'.", + "DownloadClientRTorrentSettingsUrlPathHelpText": "Pfad zum XMLRPC-Endpunkt, siehe {url}. Dies ist normalerweise RPC2 oder [Pfad zu ruTorrent]{url2}, wenn ruTorrent verwendet wird.", + "DownloadClientSettingsAddPaused": "Pausiert hinzufügen", + "SelectDownloadClientModalTitle": "{modalTitle} – Wähle Download-Client", + "DownloadClientQbittorrentSettingsFirstAndLastFirst": "Erster und Letzter Erster", + "DownloadClientQbittorrentSettingsInitialStateHelpText": "Ausgangszustand für zu qBittorrent hinzugefügte Torrents. Beachten Sie, dass erzwungene Torrents nicht den Seed-Beschränkungen unterliegen", + "IndexerSettingsAppsMinimumSeeders": "Apps Mindestanzahl von Seedern", + "IndexerHDBitsSettingsMediums": "Medien", + "Destination": "Ziel", + "Directory": "Verzeichnis", + "DownloadClientDelugeSettingsUrlBaseHelpText": "Fügt der Deluge-JSON-URL ein Präfix hinzu, siehe {url}", + "DownloadClientFloodSettingsAdditionalTags": "Zusätzliche Tags", + "DownloadClientFloodSettingsTagsHelpText": "Erste Tags eines Downloads. Um erkannt zu werden, muss ein Download über alle Anfangs-Tags verfügen. Dies vermeidet Konflikte mit nicht verwandten Downloads.", + "DownloadClientFreeboxSettingsAppToken": "App-Token", + "DownloadClientFreeboxSettingsAppTokenHelpText": "App-Token, das beim Erstellen des Zugriffs auf die Freebox-API abgerufen wird (z. B. „app_token“)", + "DownloadClientFreeboxSettingsHostHelpText": "Hostname oder Host-IP-Adresse der Freebox, standardmäßig „{url}“ (funktioniert nur im selben Netzwerk)", + "DownloadClientPneumaticSettingsNzbFolder": "NZB-Ordner", + "DownloadClientPneumaticSettingsNzbFolderHelpText": "Dieser Ordner muss über XBMC erreichbar sein", + "DownloadClientPneumaticSettingsStrmFolder": "Strm-Ordner", + "DownloadClientPneumaticSettingsStrmFolderHelpText": ".strm-Dateien in diesem Ordner werden von der Drohne importiert", + "DownloadClientQbittorrentSettingsSequentialOrder": "Fortlaufende Reihenfolge", + "DownloadClientQbittorrentSettingsSequentialOrderHelpText": "In sequentieller Reihenfolge herunterladen (qBittorrent 4.1.0+)", + "DownloadClientQbittorrentSettingsUseSslHelpText": "Verwenden Sie eine sichere Verbindung. Siehe Optionen -> Web-Benutzeroberfläche -> „HTTPS statt HTTP verwenden“ in qBittorrent.", + "DownloadClientRTorrentSettingsAddStopped": "Hinzufügen gestoppt", + "DownloadClientRTorrentSettingsAddStoppedHelpText": "Durch die Aktivierung werden Torrents und Magnete im gestoppten Zustand zu rTorrent hinzugefügt. Dadurch können Magnetdateien beschädigt werden.", + "DownloadClientRTorrentSettingsDirectoryHelpText": "Optionaler Speicherort für Downloads. Lassen Sie das Feld leer, um den standardmäßigen rTorrent-Speicherort zu verwenden", + "DownloadClientRTorrentSettingsUrlPath": "URL-Pfad", + "IndexerHDBitsSettingsCodecs": "Codecs", + "IndexerSettingsVipExpiration": "VIP Ablaufdatum", + "TorrentBlackholeSaveMagnetFilesHelpText": "Speichern Sie den Magnet-Link, wenn keine .torrent-Datei verfügbar ist (nur nützlich, wenn der Download-Client in einer Datei gespeicherte Magnete unterstützt)", + "TorrentBlackholeTorrentFolder": "Torrent-Ordner", + "UseSsl": "SSL verwenden", + "UsenetBlackholeNzbFolder": "NZB-Ordner", + "TorrentBlackholeSaveMagnetFilesExtensionHelpText": "Für Magnet-Links zu verwendende Erweiterung, standardmäßig „.magnet“.", + "XmlRpcPath": "XML-RPC-Pfad", + "ProxyValidationBadRequest": "Proxy konnte nicht getestet werden. StatusCode: {statusCode}", + "SecretToken": "Geheimer Token", + "DownloadClientDownloadStationSettingsDirectoryHelpText": "Optionaler freigegebener Ordner zum Ablegen von Downloads. Lassen Sie das Feld leer, um den Standardspeicherort der Download Station zu verwenden", + "DownloadClientFloodSettingsAdditionalTagsHelpText": "Fügt Eigenschaften von Medien als Tags hinzu. Hinweise sind Beispiele.", + "TorrentBlackholeSaveMagnetFiles": "Speicher Magnetdateien", + "TorrentBlackholeSaveMagnetFilesExtension": "Speicher die Magnet-Dateienerweiterung", + "Default": "Standard", + "GrabRelease": "Release holen", + "Script": "Skript", + "IndexerDownloadClientHealthCheckMessage": "Indexer mit ungültigen Download-Clients: {indexerNames}.", + "Any": "Beliebig", + "BuiltIn": "Eingebaut", + "PublishedDate": "Veröffentlichungsdatum", + "Redirected": "Umleiten", + "AllSearchResultsHiddenByFilter": "Alle Ergebnisse werden durch den angewendeten Filter ausgeblendet", + "DockerUpdater": "Aktualisieren Sie den Docker-Container, um das Update zu erhalten", + "Download": "Herunterladen", + "ErrorRestoringBackup": "Fehler beim Wiederherstellen der Sicherung", + "ExternalUpdater": "{appName} ist so konfiguriert, dass es einen externen Aktualisierungsmechanismus verwendet", + "NoEventsFound": "Keine Ereignisse gefunden", + "RestartReloadNote": "Hinweis: {appName} startet während des Wiederherstellungsvorgangs automatisch neu und lädt die Benutzeroberfläche neu.", + "TheLogLevelDefault": "Die Protokollebene ist standardmäßig auf „Info“ eingestellt und kann unter „Allgemeine Einstellungen“ (/settings/general) geändert werden.", + "UpdateAppDirectlyLoadError": "{appName} kann nicht direkt aktualisiert werden.", + "UpdaterLogFiles": "Updater-Protokolldateien", + "WouldYouLikeToRestoreBackup": "Willst du das Backup '{name}' wiederherstellen?", + "AptUpdater": "Verwenden Sie apt, um das Update zu installieren", + "InstallLatest": "Neueste Version installieren", + "CurrentlyInstalled": "Derzeit installiert", + "Mixed": "Gemischt", + "DownloadClientQbittorrentSettingsContentLayout": "Inhaltslayout", + "FailedToFetchSettings": "Einstellungen können nicht abgerufen werden", + "External": "Extern", + "FailedToFetchUpdates": "Updates konnten nicht abgerufen werden", + "IndexerSettingsSeedRatio": "Seed-Verhältnis", + "Install": "Installieren", + "ManualGrab": "Manuelles Greifen", + "OverrideGrabModalTitle": "Überschreiben und Abrufen - {title}", + "DownloadClientQbittorrentSettingsContentLayoutHelpText": "Ob das konfigurierte Inhaltslayout von qBittorrent, das ursprüngliche Layout des Torrents oder immer ein Unterordner erstellt werden soll (qBittorrent 4.3.2+)", + "HealthMessagesInfoBox": "Weitere Informationen zur Ursache dieser Gesundheitsprüfungsnachrichten findest du, indem du auf den Wiki-Link (Buch-Symbol) am Ende der Zeile klickst oder deine [Protokolle]({link}) überprüfst. Wenn du Schwierigkeiten hast, diese Nachrichten zu interpretieren, kannst du unseren Support kontaktieren, über die Links unten.", + "IndexerHDBitsSettingsMediumsHelpText": "Wenn nicht angegeben, werden alle Optionen verwendet.", + "InvalidUILanguage": "Die UI ist auf eine ungültige Sprache eingestellt, korrigiere sie und speichere die Einstellungen", + "LogFilesLocation": "Protokolldateien befinden sich unter: {location}", + "Logout": "Abmelden", + "NoHistoryFound": "Keine Historie gefunden", + "PasswordConfirmation": "Passwortbestätigung", + "InfoUrl": "Info-URL", + "LogSizeLimit": "Protokollgrößenlimit", + "LogSizeLimitHelpText": "Maximale Protokolldateigröße in MB, bevor archiviert wird. Standard ist 1MB.", + "NotificationsEmailSettingsUseEncryption": "Verschlüsselung verwenden", + "NotificationsTelegramSettingsIncludeAppName": "{appName} im Titel einfügen", + "NotificationsTelegramSettingsIncludeAppNameHelpText": "Optional den Nachrichtentitel mit {appName} voranstellen, um Benachrichtigungen von verschiedenen Anwendungen zu unterscheiden", + "LabelIsRequired": "Label ist erforderlich", + "Menu": "Menü", + "IndexerHDBitsSettingsCodecsHelpText": "Wenn nicht angegeben, werden alle Optionen verwendet.", + "IndexerSettingsCookie": "Cookie", + "IndexerSettingsSeedTime": "Seed-Zeit", + "IndexerSettingsAdditionalParameters": "Zusätzliche Parameter", + "IndexerSettingsApiPath": "API-Pfad", + "IndexerSettingsApiPathHelpText": "Pfad zur API, normalerweise {url}", + "DownloadClientTransmissionSettingsUrlBaseHelpText": "Fügt der {clientName}-rpc-URL ein Präfix hinzu, z. B. {url}, standardmäßig '{defaultUrl}'", + "IndexerSettingsSeedRatioHelpText": "Das Verhältnis, das ein Torrent erreichen muss, bevor er gestoppt wird. Leer verwendet das Standardverhältnis des Download-Clients. Das Verhältnis sollte mindestens 1,0 betragen und den Regeln des Indexers folgen.", + "IndexerSettingsSeedTimeHelpText": "Die Zeit, die ein Torrent gesät werden sollte, bevor er gestoppt wird. Leer verwendet die Standardzeit des Download-Clients", + "InstallMajorVersionUpdate": "Update installieren", + "InstallMajorVersionUpdateMessage": "Dieses Update wird eine neue Hauptversion installieren und ist möglicherweise nicht mit deinem System kompatibel. Bist du sicher, dass du dieses Update installieren möchtest?", + "InstallMajorVersionUpdateMessageLink": "Weitere Informationen findest du unter [{domain}]({url}).", + "NotificationsEmailSettingsUseEncryptionHelpText": "Ob bevorzugt Verschlüsselung verwendet werden soll, wenn auf dem Server konfiguriert, ob immer Verschlüsselung über SSL (nur Port 465) oder StartTLS (anderer Port) verwendet wird oder keine Verschlüsselung verwendet wird", + "PackageVersionInfo": "{packageVersion} von {packageAuthor}", + "PreviouslyInstalled": "Früher installiert", + "PrioritySettings": "Priorität: {priority}", + "SeedRatio": "Seed-Verhältnis", + "SeedTime": "Seed-Zeit", + "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "Wenn ein Torrent durch einen Hash blockiert wird, wird er möglicherweise nicht korrekt abgelehnt während RSS/Recherche für einige Indexer. Diese Option aktiviert die Ablehnung des Torrents nach dem Abrufen, aber bevor er an den Client gesendet wird.", + "IndexerHDBitsSettingsOriginsHelpText": "Wenn nicht angegeben, werden alle Optionen verwendet.", + "IndexerSettingsGrabLimit": "Grab-Limit", + "IndexerBeyondHDSettingsLimitedOnlyHelpText": "Nur nach Freeleech suchen (Begrenztes UL)", + "IndexerFileListSettingsPasskeyHelpText": "Site Passkey (Dies ist die alphanumerische Zeichenfolge in der Tracker-URL, die in deinem Download-Client angezeigt wird)", + "IndexerIPTorrentsSettingsCookieUserAgent": "Cookie-Benutzer-Agent", + "DownloadClientSettingsDefaultCategorySubFolderHelpText": "Standard-Fallback-Kategorie, wenn für eine Veröffentlichung keine zugeordnete Kategorie existiert. Das Hinzufügen einer für {appName} spezifischen Kategorie vermeidet Konflikte mit nicht verwandten {appName}-Downloads. Die Verwendung einer Kategorie ist optional, aber dringend empfohlen. Eine [Kategorie]-Unterverzeichnis wird im Ausgabeverzeichnis erstellt.", + "IndexerBeyondHDSettingsRssKeyHelpText": "RSS-Schlüssel von der Website (zu finden unter Mein Sicherheitsbereich => RSS-Schlüssel)", + "IndexerHDBitsSettingsUseFilenamesHelpText": "Aktiviere diese Option, wenn du Torrent-Dateinamen als Releasetitel verwenden möchtest", + "IndexerSettingsAppsMinimumSeedersHelpText": "Minimale benötigte Seeder von den Anwendungen, damit der Indexer greifen kann; leer ist die Standardeinstellung des Sync-Profils", + "IndexerGazelleGamesSettingsApiKeyHelpText": "API-Schlüssel von der Seite (Zu finden unter Einstellungen => Zugriffseinstellungen)", + "IndexerPassThePopcornSettingsApiUserHelpText": "Diese Einstellungen findest du in deinen PassThePopcorn-Sicherheitseinstellungen (Profil bearbeiten > Sicherheit).", + "IndexerSettingsQueryLimitHelpText": "Die maximale Anzahl an Queries, die {appName} der Seite gemäß der jeweiligen Einheit erlauben wird", + "IndexerRedactedSettingsApiKeyHelpText": "API-Schlüssel von der Seite (Zu finden unter Einstellungen => Zugriffseinstellungen)", + "IndexerSettingsQueryLimit": "Query Limit", + "PackSeedTimeHelpText": "Die Zeit, die ein Pack (Season oder Diskographie)-Torrent gesät werden soll, bevor er gestoppt wird. Leer ist die Standardeinstellung der App", + "TotalIndexerSuccessfulGrabs": "Gesamtanzahl erfolgreicher Indexer-Suchanfragen", + "ProwlarrDownloadClientsInAppOnlyAlert": "Download-Clients sind nur für In-App-Suchen in {appName} und synchronisieren sich nicht mit Apps. Es sind keine Pläne vorgesehen, eine solche Funktionalität hinzuzufügen.", + "TotalUserAgentGrabs": "Gesamtanzahl der User-Agent-Grabs", + "DefaultCategory": "Standardkategorie", + "IndexerDownloadClientHelpText": "Gib an, welcher Download-Client für Grab-Vorgänge, die innerhalb von {appName} von diesem Indexer durchgeführt werden, verwendet wird", + "IndexerHistoryLoadError": "Fehler beim Laden der Indexer-Historie", + "IndexerNzbIndexSettingsApiKeyHelpText": "Website-API-Key", + "IndexerPassThePopcornSettingsApiKeyHelpText": "Website-API-Key", + "IndexerPassThePopcornSettingsGoldenPopcornOnlyHelpText": "Sucher nur Golden Popcorn Releases", + "IndexerSettingsCookieHelpText": "Website Cookie", + "IndexerSettingsPackSeedTimeIndexerHelpText": "Die Zeit, die ein Pack (Season oder Diskographie)-Torrent gesät werden soll, bevor er gestoppt wird. Leer ist die Standardeinstellung der App", + "SearchAllIndexers": "Alle Indexer durchsuchen", + "SearchCountIndexers": "Suche {count} Indexer(s)", + "SeedTimeHelpText": "Die Zeit, die ein Torrent gesät werden soll, bevor er gestoppt wird. Leer ist die Standardeinstellung der App", + "IndexerGazelleGamesSettingsFreeleechOnlyHelpText": "Suche nur Freeleech-Releases", + "IndexerNewznabSettingsVipExpirationHelpText": "Gib das Datum (yyyy-mm-dd) für das VIP-Ablaufdatum ein oder lasse es leer, {appName} benachrichtigt eine Woche vor Ablauf des VIP", + "ProxyValidationUnableToConnect": "Kann nicht mit dem Proxy verbunden werden: {exceptionMessage}. Überprüfe das Protokoll rund um diesen Fehler für Details", + "IndexerId": "Indexer ID", + "OnGrabHelpText": "Bei Release Grab", + "AuthQueries": "Authentifizierungsanfragen", + "PackSeedTime": "Pack-Seed-Zeit", + "DeleteSelectedApplications": "Ausgewählte Anwendungen löschen", + "DownloadClientSettingsDefaultCategoryHelpText": "Standard-Fallback-Kategorie, wenn für eine Veröffentlichung keine zugeordnete Kategorie existiert. Das Hinzufügen einer für {appName} spezifischen Kategorie vermeidet Konflikte mit nicht verwandten {appName}-Downloads. Die Verwendung einer Kategorie ist optional, aber dringend empfohlen.", + "FoundCountReleases": "{itemCount} Veröffentlichungen gefunden", + "IncludeManualGrabsHelpText": "Manuelle Abrufe, die innerhalb von {appName} gemacht wurden, einbeziehen", + "IndexerAlphaRatioSettingsFreeleechOnlyHelpText": "Nur nach Freeleech-Veröffentlichungen suchen", + "IndexerBeyondHDSettingsApiKeyHelpText": "API-Schlüssel von der Website (zu finden in „Meine Sicherheit“ => „API-Schlüssel“)", + "IndexerBeyondHDSettingsFreeleechOnlyHelpText": "Nur nach Freeleech-Releases suchen", + "IndexerBeyondHDSettingsRefundOnlyHelpText": "Nur nach Rückerstattungen suchen", + "IndexerDisabled": "Indexer deaktiviert", + "IndexerFileListSettingsFreeleechOnlyHelpText": "Suche nur Freeleech-Releases", + "IndexerGazelleGamesSettingsSearchGroupNamesHelpText": "Suche Veröffentlichungen nach Gruppennamen", + "IndexerNebulanceSettingsApiKeyHelpText": "API-Schlüssel aus den Benutzereinstellungen > API-Schlüssel. Der Schlüssel muss List- und Download-Berechtigungen haben", + "IndexerSettingsGrabLimitHelpText": "Die maximale Anzahl an Grabs, die {appName} der Seite erlauben wird, wie von der jeweiligen Einheit festgelegt", + "IndexerSettingsLimitsUnit": "Limits-Einheit", + "IndexerSettingsLimitsUnitHelpText": "Die Zeiteinheit zur Berechnung der Limits pro Indexer", + "IndexerStatus": "Indexer Status", + "LastFailure": "Letzter Fehler", + "ManageApplications": "Applikationen verwalten", + "NoApplicationsFound": "Keine Applikationen gefunden", + "NoIndexerCategories": "Keine Kategorien für diesen Indexer gefunden", + "NoIndexerHistory": "Keine Historie für diesen Indexer gefunden", + "Open": "Offen", + "OverrideAndAddToDownloadClient": "Überschreiben und zum Download-Client hinzufügen", + "PreferMagnetUrl": "Magnet URL bevorzugen", + "PreferMagnetUrlHelpText": "Wenn aktiviert, wird dieser Indexer die Verwendung von Magnet-URLs für Grabs bevorzugen, mit Rückfall auf Torrent-Links", + "RssQueries": "RSS Anfragen", + "TotalHostGrabs": "Gesamtanzahl der Host-Grabs", + "TotalHostQueries": "Gesamtanzahl der Host-Suchanfragen", + "SeedRatioHelpText": "Das Verhältnis, das ein Torrent erreichen sollte, bevor er gestoppt wird. Leer ist die Standardeinstellung der App", + "AverageGrabs": "Durchschnittliche Abrufe", + "AverageQueries": "Durchschnittliche Anfragen", + "SelectedCountOfCountReleases": "Ausgewählt {selectedCount} von {itemCount} Releases", + "NewznabUrl": "Newznab Url", + "QueryType": "Abfragetyp", + "DisabledUntil": "Deaktiviert bis", + "MappedCategories": "Zuordnete Kategorien", + "AreYouSureYouWantToDeleteIndexer": "Bist du sicher, dass du „{name}“ aus {appName} löschen möchtest?", + "TotalIndexerQueries": "Gesamtanzahl der Indexer-Suchanfragen", + "ProwlarrDownloadClientsAlert": "Wenn du beabsichtigst, direkt innerhalb von {appName} zu suchen, musst du Download-Clients hinzufügen. Andernfalls musst du sie hier nicht hinzufügen. Für Suchen aus deinen Apps werden stattdessen die dort konfigurierten Download-Clients verwendet.", + "AppsMinimumSeedersHelpText": "Mindestanzahl an Seedern, die von der Anwendung für den Indexer erforderlich ist, um herunterzuladen. Leer bedeutet, dass das Standardprofil der Synchronisierung verwendet wird", + "CountIndexersAvailable": "{count} Indexer verfügbar", + "DeleteClientCategory": "Download-Client-Kategorie löschen", + "DeleteSelectedIndexer": "Ausgewählten Indexer löschen", + "TotalGrabs": "Gesamtanzahl der Grabs", + "DownloadClientCategory": "Download-Client-Kategorie", + "EditCategory": "Kategorie bearbeiten", + "IndexerSettingsApiUser": "API Benutzer", + "RssFeed": "RSS Feed", + "InitialFailure": "Initialer Fehler", + "ApplicationSettingsSyncRejectBlocklistedTorrentHashes": "Blockierte Torrent-Hashes beim Abrufen synchron ablehnen", + "DeleteSelectedIndexers": "Ausgewählte Indexer löschen", + "IndexerHDBitsSettingsPasskeyHelpText": "Passkey aus den Benutzerdetails", + "IndexerSettingsPasskey": "Pass Key", + "ClickToChangeQueryOptions": "Klicken, um Abfrageoptionen zu ändern", + "IndexerCategories": "Indexer-Kategorien", + "SearchQueries": "Suchanfragen", + "IndexerAlphaRatioSettingsExcludeScene": "SCENE ausschließen", + "IndexerAlphaRatioSettingsExcludeSceneHelpText": "SCENE-Veröffentlichungen aus den Ergebnissen ausschließen", + "IndexerBeyondHDSettingsRefundOnly": "Nur Rückerstattung", + "IndexerFileListSettingsUsernameHelpText": "Website-Benutzername", + "IndexerGazelleGamesSettingsApiKeyHelpTextWarning": "Muss Benutzer- und Torrents-Berechtigungen haben", + "IndexerHDBitsSettingsFreeleechOnlyHelpText": "Zeige nur Freeleech-Releases", + "IndexerHDBitsSettingsUsernameHelpText": "Webseite-Benutzername", + "IndexerIPTorrentsSettingsFreeleechOnlyHelpText": "Suche nur Freeleech-Releases", + "IndexerNewznabSettingsApiKeyHelpText": "Website API Key", + "IndexerOrpheusSettingsApiKeyHelpText": "API-Schlüssel von der Seite (Zu finden unter Einstellungen => Zugriffseinstellungen)", + "IndexerPassThePopcornSettingsFreeleechOnlyHelpText": "Suche nur Freeleech-Releases", + "IndexerTorrentSyndikatSettingsApiKeyHelpText": "Website-API-Schlüssel", + "AreYouSureYouWantToDeleteCategory": "Bist du sicher, dass du die zugeordnete Kategorie löschen möchtest?", + "DownloadClientSettingsPriorityItemHelpText": "Priorität, die beim Abrufen von Elementen verwendet werden soll", + "GoToApplication": "Zur Anwendung gehen", + "HistoryDetails": "Historie-Details", + "IndexerBeyondHDSettingsLimitedOnly": "Nur begrenzt", + "IndexerHDBitsSettingsOrigins": "Ursprünge", + "IndexerHDBitsSettingsUseFilenames": "Verwende Dateinamen", + "IndexerIPTorrentsSettingsCookieUserAgentHelpText": "User-Agent, der mit dem Cookie aus dem Browser verwendet wird", + "IndexerNewznabSettingsAdditionalParametersHelpText": "Zusätzliche Newznab-Parameter", + "IndexerSettingsPackSeedTime": "Pack-Seed-Zeit", + "IndexerSettingsRssKey": "RSS Schlüssel", + "IndexerMTeamTpSettingsApiKeyHelpText": "API-Schlüssel von der Seite (Zu finden im Benutzersteuerungsfeld => Sicherheit => Labor)", + "IndexerMTeamTpSettingsFreeleechOnlyHelpText": "Suche nur Freeleech-Releases", + "IndexerSettingsFreeleechOnly": "Nur Freeleech", + "IndexerSettingsPreferMagnetUrl": "Magnet-URL bevorzugen", + "IndexerSettingsPreferMagnetUrlHelpText": "Wenn aktiviert, bevorzugt dieser Indexer die Verwendung von Magnet-URLs für Grabs mit Rückfall auf Torrent-Links", + "TorznabUrl": "Torznab Url", + "IndexerPassThePopcornSettingsGoldenPopcornOnly": "Nur Golden Popcorn", + "IndexerSettingsBaseUrl": "Basis Url", + "IndexerAvistazSettingsFreeleechOnlyHelpText": "Nur nach Freeleech-Veröffentlichungen suchen", + "IndexerAvistazSettingsPasswordHelpText": "Website-Passwort", + "IndexerAvistazSettingsPidHelpText": "PID aus der „Mein Konto“- oder „Mein Profil“-Seite", + "IndexerAvistazSettingsUsernameHelpText": "Website-Benutzername", + "IndexerAvistazSettingsUsernameHelpTextWarning": "Nur Mitglieder mit Rang „Mitglied“ und höher können die API auf diesem Indexer nutzen.", + "IndexerBeyondHDSettingsRewindOnly": "Nur zurückspulen", + "IndexerBeyondHDSettingsRewindOnlyHelpText": "Nur Rückwärtssuche", + "IndexerBeyondHDSettingsSearchTypesHelpText": "Wähle die Arten von Veröffentlichungen aus, die dich interessieren. Wenn keine ausgewählt sind, werden alle Optionen verwendet.", + "IndexerFailureRate": "Indexer-Fehlerrate", + "IndexerGazelleGamesSettingsSearchGroupNames": "Suche Gruppennamen", + "IndexerSettingsBaseUrlHelpText": "Wähle die Basis-Url aus, die {appName} für Anfragen an die Seite verwenden soll", + "RepeatSearch": "Suche wiederholen", + "AverageResponseTimesMs": "Durchschnittliche Indexer-Antwortzeiten (ms)", + "BasicSearch": "Einfache Suche" } diff --git a/src/NzbDrone.Core/Localization/Core/el.json b/src/NzbDrone.Core/Localization/Core/el.json index 18edee506..4c6187fcc 100644 --- a/src/NzbDrone.Core/Localization/Core/el.json +++ b/src/NzbDrone.Core/Localization/Core/el.json @@ -135,7 +135,7 @@ "ErrorLoadingContents": "Σφάλμα κατά τη φόρτωση περιεχομένων", "GeneralSettings": "Γενικές Ρυθμίσεις", "Grabs": "Αρπάζω", - "HealthNoIssues": "Δεν υπάρχουν προβλήματα με τη διαμόρφωσή σας", + "NoIssuesWithYourConfiguration": "Δεν υπάρχουν προβλήματα με τη διαμόρφωσή σας", "HomePage": "Αρχική σελίδα", "Host": "Πλήθος", "Hostname": "Όνομα κεντρικού υπολογιστή", @@ -196,7 +196,7 @@ "SSLCertPath": "Διαδρομή πιστοποίησης SSL", "StartTypingOrSelectAPathBelow": "Ξεκινήστε να πληκτρολογείτε ή επιλέξτε μια διαδρομή παρακάτω", "Style": "Στυλ", - "SystemTimeCheckMessage": "Ο χρόνος συστήματος είναι απενεργοποιημένος για περισσότερο από 1 ημέρα. Οι προγραμματισμένες εργασίες ενδέχεται να μην εκτελούνται σωστά έως ότου διορθωθεί η ώρα", + "SystemTimeHealthCheckMessage": "Ο χρόνος συστήματος είναι απενεργοποιημένος για περισσότερο από 1 ημέρα. Οι προγραμματισμένες εργασίες ενδέχεται να μην εκτελούνται σωστά έως ότου διορθωθεί η ώρα", "TableOptions": "Επιλογές πίνακα", "TableOptionsColumnsMessage": "Επιλέξτε ποιες στήλες είναι ορατές και με ποια σειρά εμφανίζονται", "TagIsNotUsedAndCanBeDeleted": "Η ετικέτα δεν χρησιμοποιείται και μπορεί να διαγραφεί", @@ -234,7 +234,7 @@ "IncludeHealthWarningsHelpText": "Συμπεριλάβετε προειδοποιήσεις για την υγεία", "Security": "Ασφάλεια", "Tasks": "Καθήκοντα", - "UnableToLoadBackups": "Δεν είναι δυνατή η φόρτωση αντιγράφων ασφαλείας", + "BackupsLoadError": "Δεν είναι δυνατή η φόρτωση αντιγράφων ασφαλείας", "DownloadClientsLoadError": "Δεν είναι δυνατή η φόρτωση πελατών λήψης", "UpdateMechanismHelpText": "Χρησιμοποιήστε το ενσωματωμένο πρόγραμμα ενημέρωσης του {appName} ή ένα script", "AnalyticsEnabledHelpText": "Στείλτε ανώνυμες πληροφορίες χρήσης και σφάλματος στους διακομιστές του {appName}. Αυτό περιλαμβάνει πληροφορίες στο πρόγραμμα περιήγησής σας, ποιες σελίδες {appName} WebUI χρησιμοποιείτε, αναφορά σφαλμάτων καθώς και έκδοση λειτουργικού συστήματος και χρόνου εκτέλεσης. Θα χρησιμοποιήσουμε αυτές τις πληροφορίες για να δώσουμε προτεραιότητα σε λειτουργίες και διορθώσεις σφαλμάτων.", @@ -431,7 +431,7 @@ "IndexerInfo": "Πληροφορίες ευρετηρίου", "IndexerName": "Όνομα ευρετηρίου", "IndexerProxies": "Proxer Indexer", - "IndexerNoDefCheckMessage": "Τα ευρετήρια δεν έχουν ορισμό και δεν θα λειτουργήσουν: {0}. Αφαιρέστε και (ή) προσθέστε ξανά στο {appName}", + "IndexerNoDefinitionCheckHealthCheckMessage": "Τα ευρετήρια δεν έχουν ορισμό και δεν θα λειτουργήσουν: {indexerNames}. Αφαιρέστε και (ή) προσθέστε ξανά στο {appName}", "SemiPrivate": "Ημι-ιδιωτικό", "SettingsIndexerLoggingHelpText": "Καταγραφή πρόσθετων δεδομένων ευρετηρίου συμπεριλαμβανομένης της απόκρισης", "SearchTypes": "Τύποι αναζήτησης", @@ -458,7 +458,7 @@ "SyncLevelFull": "Πλήρης συγχρονισμός: Θα διατηρήσει πλήρως συγχρονισμένα τα ευρετήρια αυτής της εφαρμογής. Στη συνέχεια, οι αλλαγές που γίνονται στους indexers στο {appName} συγχρονίζονται με αυτήν την εφαρμογή. Οποιαδήποτε αλλαγή γίνει σε ευρετήρια απομακρυσμένα σε αυτήν την εφαρμογή θα παρακαμφθεί από τον {appName} στον επόμενο συγχρονισμό.", "Remove": "Αφαιρώ", "Replace": "Αντικαθιστώ", - "TheLatestVersionIsAlreadyInstalled": "Η τελευταία έκδοση του {appName} είναι ήδη εγκατεστημένη", + "OnLatestVersion": "Η τελευταία έκδοση του {appName} είναι ήδη εγκατεστημένη", "ApiKeyValidationHealthCheckMessage": "Παρακαλούμε ενημερώστε το κλείδι API ώστε να έχει τουλάχιστον {length} χαρακτήρες. Μπορείτε να το κάνετε αυτό μέσα από τις ρυθμίσεις ή το αρχείο ρυθμίσεων", "StopSelecting": "Διακοπή Επιλογής", "OnHealthRestored": "Στην Αποκατάσταση Υγείας", @@ -522,5 +522,23 @@ "IndexerBeyondHDSettingsSearchTypes": "Τύποι αναζήτησης", "IndexerHDBitsSettingsMediums": "Μεσαίο", "UseSsl": "Χρησιμοποιήστε SSL", - "CustomFilter": "Custom Φιλτρα" + "CustomFilter": "Custom Φιλτρα", + "GrabRelease": "Πιάσε την απελευθέρωση", + "ProxyValidationBadRequest": "Αποτυχία δοκιμής διακομιστή μεσολάβησης. StatusCode: {statusCode}", + "Script": "Γραφή", + "BuiltIn": "Ενσωματωμένο", + "PublishedDate": "Ημερομηνία δημοσίευσης", + "Redirected": "Διευθύνω πάλιν", + "AllSearchResultsHiddenByFilter": "Όλα τα αποτελέσματα αποκρύπτονται από το εφαρμοσμένο φίλτρο", + "Download": "Κατεβάστε", + "ErrorRestoringBackup": "Σφάλμα κατά την επαναφορά του αντιγράφου ασφαλείας", + "ExternalUpdater": "Το {appName} έχει ρυθμιστεί να χρησιμοποιεί έναν εξωτερικό μηχανισμό ενημέρωσης", + "NoEventsFound": "Δεν βρέθηκαν συμβάντα", + "RestartReloadNote": "Σημείωση: Το {appName} θα επανεκκινήσει αυτόματα και θα φορτώσει ξανά το περιβάλλον εργασίας χρήστη κατά τη διαδικασία επαναφοράς.", + "UpdateAppDirectlyLoadError": "Δεν είναι δυνατή η απευθείας ενημέρωση του {appName},", + "DockerUpdater": "ενημερώστε το κοντέινερ για να λάβετε την ενημέρωση", + "AptUpdater": "Χρησιμοποιήστε το apt για να εγκαταστήσετε την ενημέρωση", + "InstallLatest": "Εγκατάσταση πιο πρόσφατου", + "CurrentlyInstalled": "Εγκατεστημένο αυτήν τη στιγμή", + "Mixed": "Σταθερός" } diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index 794f46c20..2565fdf01 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -31,8 +31,10 @@ "Album": "Album", "All": "All", "AllIndexersHiddenDueToFilter": "All indexers are hidden due to applied filter.", + "AllSearchResultsHiddenByFilter": "All search results are hidden by the applied filter.", "Analytics": "Analytics", "AnalyticsEnabledHelpText": "Send anonymous usage and error information to {appName}'s servers. This includes information on your browser, which {appName} WebUI pages you use, error reporting as well as OS and runtime version. We will use this information to prioritize features and bug fixes.", + "Any": "Any", "ApiKey": "API Key", "ApiKeyValidationHealthCheckMessage": "Please update your API key to be at least {length} characters long. You can do this via settings or the config file", "AppDataDirectory": "AppData Directory", @@ -66,6 +68,7 @@ "Apps": "Apps", "AppsMinimumSeeders": "Apps Minimum Seeders", "AppsMinimumSeedersHelpText": "Minimum seeders required by the Applications for the indexer to grab, empty is Sync profile's default", + "AptUpdater": "Use apt to install the update", "AreYouSureYouWantToDeleteCategory": "Are you sure you want to delete mapped category?", "AreYouSureYouWantToDeleteIndexer": "Are you sure you want to delete '{name}' from {appName}?", "Artist": "Artist", @@ -87,6 +90,8 @@ "Author": "Author", "Automatic": "Automatic", "AutomaticSearch": "Automatic Search", + "AverageGrabs": "Average Grabs", + "AverageQueries": "Average Queries", "AverageResponseTimesMs": "Average Indexer Response Times (ms)", "Backup": "Backup", "BackupFolderHelpText": "Relative paths will be under {appName}'s AppData directory", @@ -94,6 +99,7 @@ "BackupNow": "Backup Now", "BackupRetentionHelpText": "Automatic backups older than the retention period will be cleaned up automatically", "Backups": "Backups", + "BackupsLoadError": "Unable to load backups", "BasicSearch": "Basic Search", "BeforeUpdate": "Before update", "BindAddress": "Bind Address", @@ -105,6 +111,7 @@ "Branch": "Branch", "BranchUpdate": "Branch to use to update {appName}", "BranchUpdateMechanism": "Branch used by external update mechanism", + "BuiltIn": "Built-In", "BypassProxyForLocalAddresses": "Bypass Proxy for Local Addresses", "Cancel": "Cancel", "CancelPendingTask": "Are you sure you want to cancel this pending task?", @@ -136,6 +143,7 @@ "CountDownloadClientsSelected": "{count} download client(s) selected", "CountIndexersAvailable": "{count} indexer(s) available", "CountIndexersSelected": "{count} indexer(s) selected", + "CurrentlyInstalled": "Currently Installed", "Custom": "Custom", "CustomFilter": "Custom Filter", "CustomFilters": "Custom Filters", @@ -179,8 +187,10 @@ "DisabledUntil": "Disabled Until", "Discord": "Discord", "Docker": "Docker", + "DockerUpdater": "Update the docker container to receive the update", "Donate": "Donate", "Donations": "Donations", + "Download": "Download", "DownloadClient": "Download Client", "DownloadClientAriaSettingsDirectoryHelpText": "Optional location to put downloads in, leave blank to use the default Aria2 location", "DownloadClientCategory": "Download Client Category", @@ -231,6 +241,7 @@ "DownloadClientStatusSingleClientHealthCheckMessage": "Download clients unavailable due to failures: {downloadClientNames}", "DownloadClientTransmissionSettingsDirectoryHelpText": "Optional location to put downloads in, leave blank to use the default Transmission location", "DownloadClientTransmissionSettingsUrlBaseHelpText": "Adds a prefix to the {clientName} rpc url, eg {url}, defaults to '{defaultUrl}'", + "DownloadClientUTorrentProviderMessage": "uTorrent has a history of including cryptominers, malware and ads, we strongly encourage you to choose a different client.", "DownloadClients": "Download Clients", "DownloadClientsLoadError": "Unable to load download clients", "DownloadClientsSettingsSummary": "Download clients configuration for integration into {appName} UI search", @@ -264,12 +275,16 @@ "Episode": "Episode", "Error": "Error", "ErrorLoadingContents": "Error loading contents", + "ErrorRestoringBackup": "Error restoring backup", "EventType": "Event Type", "Events": "Events", "Exception": "Exception", "ExistingTag": "Existing tag", "External": "External", + "ExternalUpdater": "{appName} is configured to use an external update mechanism", "Failed": "Failed", + "FailedToFetchSettings": "Failed to fetch settings", + "FailedToFetchUpdates": "Failed to fetch updates", "FeatureRequests": "Feature Requests", "Filename": "Filename", "Files": "Files", @@ -293,7 +308,7 @@ "Grabbed": "Grabbed", "Grabs": "Grabs", "Health": "Health", - "HealthNoIssues": "No issues with your configuration", + "HealthMessagesInfoBox": "You can find more information about the cause of these health check messages by clicking the wiki link (book icon) at the end of the row, or by checking your [logs]({link}). If you have difficulty interpreting these messages then you can reach out to our support, at the links below.", "HideAdvanced": "Hide Advanced", "History": "History", "HistoryCleanup": "History Cleanup", @@ -315,6 +330,11 @@ "IndexerAlphaRatioSettingsFreeleechOnlyHelpText": "Search freeleech releases only", "IndexerAlreadySetup": "At least one instance of indexer is already setup", "IndexerAuth": "Indexer Auth", + "IndexerAvistazSettingsFreeleechOnlyHelpText": "Search freeleech releases only", + "IndexerAvistazSettingsPasswordHelpText": "Site Password", + "IndexerAvistazSettingsPidHelpText": "PID from My Account or My Profile page", + "IndexerAvistazSettingsUsernameHelpText": "Site Username", + "IndexerAvistazSettingsUsernameHelpTextWarning": "Only member rank and above can use the API on this indexer.", "IndexerBeyondHDSettingsApiKeyHelpText": "API Key from the Site (Found in My Security => API Key)", "IndexerBeyondHDSettingsFreeleechOnlyHelpText": "Search freeleech releases only", "IndexerBeyondHDSettingsLimitedOnly": "Limited Only", @@ -368,13 +388,15 @@ "IndexerNewznabSettingsAdditionalParametersHelpText": "Additional Newznab parameters", "IndexerNewznabSettingsApiKeyHelpText": "Site API Key", "IndexerNewznabSettingsVipExpirationHelpText": "Enter date (yyyy-mm-dd) for VIP Expiration or blank, {appName} will notify 1 week from expiration of VIP", - "IndexerNoDefCheckMessage": "Indexers have no definition and will not work: {0}. Please remove and (or) re-add to {appName}", + "IndexerNoDefinitionCheckHealthCheckMessage": "Indexers have no definition and will not work: {indexerNames}. Please remove and (or) re-add to {appName}.", "IndexerNzbIndexSettingsApiKeyHelpText": "Site API Key", "IndexerObsoleteCheckMessage": "Indexers are obsolete or have been updated: {0}. Please remove and (or) re-add to {appName}", "IndexerOrpheusSettingsApiKeyHelpText": "API Key from the Site (Found in Settings => Access Settings)", "IndexerPassThePopcornSettingsApiKeyHelpText": "Site API Key", "IndexerPassThePopcornSettingsApiUserHelpText": "These settings are found in your PassThePopcorn security settings (Edit Profile > Security).", "IndexerPassThePopcornSettingsFreeleechOnlyHelpText": "Search freeleech releases only", + "IndexerPassThePopcornSettingsGoldenPopcornOnly": "Golden Popcorn only", + "IndexerPassThePopcornSettingsGoldenPopcornOnlyHelpText": "Search Golden Popcorn releases only", "IndexerPriority": "Indexer Priority", "IndexerPriorityHelpText": "Indexer Priority from 1 (Highest) to 50 (Lowest). Default: 25.", "IndexerProxies": "Indexer Proxies", @@ -402,6 +424,8 @@ "IndexerSettingsPackSeedTime": "Pack Seed Time", "IndexerSettingsPackSeedTimeIndexerHelpText": "The time a pack (season or discography) torrent should be seeded before stopping, empty is app's default", "IndexerSettingsPasskey": "Pass Key", + "IndexerSettingsPreferMagnetUrl": "Prefer Magnet URL", + "IndexerSettingsPreferMagnetUrlHelpText": "When enabled, this indexer will prefer the use of magnet URLs for grabs with fallback to torrent links", "IndexerSettingsQueryLimit": "Query Limit", "IndexerSettingsQueryLimitHelpText": "The number of max queries as specified by the respective unit that {appName} will allow to the site", "IndexerSettingsRssKey": "RSS Key", @@ -422,7 +446,13 @@ "IndexerVipExpiringHealthCheckMessage": "Indexer VIP benefits expiring soon: {indexerNames}", "Indexers": "Indexers", "Info": "Info", + "InfoUrl": "Info URL", "InitialFailure": "Initial Failure", + "Install": "Install", + "InstallLatest": "Install Latest", + "InstallMajorVersionUpdate": "Install Update", + "InstallMajorVersionUpdateMessage": "This update will install a new major version and may not be compatible with your system. Are you sure you want to install this update?", + "InstallMajorVersionUpdateMessageLink": "Please check [{domain}]({url}) for more information.", "InstanceName": "Instance Name", "InstanceNameHelpText": "Instance name in tab and for Syslog app name", "InteractiveSearch": "Interactive Search", @@ -440,9 +470,13 @@ "Level": "Level", "Link": "Link", "LogFiles": "Log Files", + "LogFilesLocation": "Log files are located in: {location}", "LogLevel": "Log Level", "LogLevelTraceHelpTextWarning": "Trace logging should only be enabled temporarily", + "LogSizeLimit": "Log Size Limit", + "LogSizeLimitHelpText": "Maximum log file size in MB before archiving. Default is 1MB.", "Logging": "Logging", + "Logout": "Logout", "Logs": "Logs", "MIA": "MIA", "MaintenanceRelease": "Maintenance Release: bug fixes and other improvements. See Github Commit History for more details", @@ -479,10 +513,12 @@ "NoChange": "No Change", "NoChanges": "No Changes", "NoDownloadClientsFound": "No download clients found", + "NoEventsFound": "No events found", "NoHistoryFound": "No history found", "NoIndexerCategories": "No categories found for this indexer", "NoIndexerHistory": "No history found for this indexer", "NoIndexersFound": "No indexers found", + "NoIssuesWithYourConfiguration": "No issues with your configuration", "NoLeaveIt": "No, Leave It", "NoLinks": "No Links", "NoLogFiles": "No log files", @@ -511,6 +547,7 @@ "OnHealthIssueHelpText": "On Health Issue", "OnHealthRestored": "On Health Restored", "OnHealthRestoredHelpText": "On Health Restored", + "OnLatestVersion": "The latest version of {appName} is already installed", "Open": "Open", "OpenBrowserOnStart": "Open browser on start", "OpenThisModal": "Open This Modal", @@ -520,6 +557,7 @@ "PackSeedTime": "Pack Seed Time", "PackSeedTimeHelpText": "The time a pack (season or discography) torrent should be seeded before stopping, empty is app's default", "PackageVersion": "Package Version", + "PackageVersionInfo": "{packageVersion} by {packageAuthor}", "PageSize": "Page Size", "PageSizeHelpText": "Number of items to show on each page", "Parameters": "Parameters", @@ -531,7 +569,10 @@ "PendingChangesStayReview": "Stay and review changes", "Port": "Port", "PortNumber": "Port Number", + "PreferMagnetUrl": "Prefer Magnet URL", + "PreferMagnetUrlHelpText": "When enabled, this indexer will prefer the use of magnet URLs for grabs with fallback to torrent links", "Presets": "Presets", + "PreviouslyInstalled": "Previously Installed", "Priority": "Priority", "PrioritySettings": "Priority: {priority}", "Privacy": "Privacy", @@ -553,6 +594,7 @@ "ProxyValidationBadRequest": "Failed to test proxy. Status code: {statusCode}", "ProxyValidationUnableToConnect": "Unable to connect to proxy: {exceptionMessage}. Check the log surrounding this error for details", "Public": "Public", + "PublishedDate": "Published Date", "Publisher": "Publisher", "Query": "Query", "QueryOptions": "Query Options", @@ -566,6 +608,7 @@ "Reddit": "Reddit", "Redirect": "Redirect", "RedirectHelpText": "Redirect incoming download request for indexer and pass the grab directly instead of proxying the request via {appName}", + "Redirected": "Redirected", "Refresh": "Refresh", "RefreshMovie": "Refresh movie", "ReleaseBranchCheckOfficialBranchMessage": "Branch {0} is not a valid {appName} release branch, you will not receive updates", @@ -583,6 +626,7 @@ "Restart": "Restart", "RestartNow": "Restart Now", "RestartProwlarr": "Restart {appName}", + "RestartReloadNote": "Note: {appName} will automatically restart and reload the UI during the restore process.", "RestartRequiredHelpTextWarning": "Requires restart to take effect", "Restore": "Restore", "RestoreBackup": "Restore Backup", @@ -601,6 +645,7 @@ "SaveChanges": "Save Changes", "SaveSettings": "Save Settings", "Scheduled": "Scheduled", + "Script": "Script", "ScriptPath": "Script Path", "Search": "Search", "SearchAllIndexers": "Search all indexers", @@ -664,7 +709,7 @@ "SyncProfile": "Sync Profile", "SyncProfiles": "Sync Profiles", "System": "System", - "SystemTimeCheckMessage": "System time is off by more than 1 day. Scheduled tasks may not run correctly until the time is corrected", + "SystemTimeHealthCheckMessage": "System time is off by more than 1 day. Scheduled tasks may not run correctly until the time is corrected", "TVSearchTypes": "TV Search Types", "TableOptions": "Table Options", "TableOptionsColumnsMessage": "Choose which columns are visible and which order they appear in", @@ -679,7 +724,7 @@ "TestAllApps": "Test All Apps", "TestAllClients": "Test All Clients", "TestAllIndexers": "Test All Indexers", - "TheLatestVersionIsAlreadyInstalled": "The latest version of {appName} is already installed", + "TheLogLevelDefault": "The log level defaults to 'Info' and can be changed in [General Settings](/settings/general)", "Theme": "Theme", "ThemeHelpText": "Change Application UI Theme, 'Auto' Theme will use your OS Theme to set Light or Dark mode. Inspired by {inspiredBy}.", "Time": "Time", @@ -719,7 +764,6 @@ "UnableToAddANewIndexerProxyPleaseTryAgain": "Unable to add a new indexer proxy, please try again.", "UnableToAddANewNotificationPleaseTryAgain": "Unable to add a new notification, please try again.", "UnableToLoadAppProfiles": "Unable to load app profiles", - "UnableToLoadBackups": "Unable to load backups", "UnableToLoadDevelopmentSettings": "Unable to load Development settings", "UnableToLoadGeneralSettings": "Unable to load General settings", "UnableToLoadHistory": "Unable to load history", @@ -730,13 +774,15 @@ "UnableToLoadUISettings": "Unable to load UI settings", "UnsavedChanges": "Unsaved Changes", "UnselectAll": "Unselect All", + "UpdateAppDirectlyLoadError": "Unable to update {appName} directly,", "UpdateAutomaticallyHelpText": "Automatically download and install updates. You will still be able to install from System: Updates", - "UpdateAvailableHealthCheckMessage": "New update is available", + "UpdateAvailableHealthCheckMessage": "New update is available: {version}", "UpdateMechanismHelpText": "Use {appName}'s built-in updater or a script", "UpdateScriptPathHelpText": "Path to a custom script that takes an extracted update package and handle the remainder of the update process", "UpdateStartupNotWritableHealthCheckMessage": "Cannot install update because startup folder '{startupFolder}' is not writable by the user '{userName}'.", "UpdateStartupTranslocationHealthCheckMessage": "Cannot install update because startup folder '{startupFolder}' is in an App Translocation folder.", "UpdateUiNotWritableHealthCheckMessage": "Cannot install update because UI folder '{uiFolder}' is not writable by the user '{userName}'.", + "UpdaterLogFiles": "Updater Log Files", "Updates": "Updates", "Uptime": "Uptime", "Url": "Url", @@ -754,6 +800,7 @@ "Website": "Website", "WhatsNew": "What's New?", "Wiki": "Wiki", + "WouldYouLikeToRestoreBackup": "Would you like to restore the backup '{name}'?", "XmlRpcPath": "XML RPC Path", "Year": "Year", "Yes": "Yes", diff --git a/src/NzbDrone.Core/Localization/Core/es.json b/src/NzbDrone.Core/Localization/Core/es.json index a49c199b7..e7981a69e 100644 --- a/src/NzbDrone.Core/Localization/Core/es.json +++ b/src/NzbDrone.Core/Localization/Core/es.json @@ -48,7 +48,7 @@ "Scheduled": "Programado", "SaveChanges": "Guardar cambios", "RestoreBackup": "Restaurar copia de seguridad", - "ReleaseBranchCheckOfficialBranchMessage": "Las versión {0} no es una versión válida de {appName}, no recibirás actualizaciones", + "ReleaseBranchCheckOfficialBranchMessage": "La rama {0} no es una rama de lanzamiento válida de {appName}, no recibirás actualizaciones", "Refresh": "Actualizar", "Queue": "Cola", "ProxyResolveIpHealthCheckMessage": "No se pudo resolver la dirección IP del Host Proxy configurado {proxyHostName}", @@ -73,7 +73,7 @@ "LastWriteTime": "Última Fecha de Escritura", "IndexerStatusUnavailableHealthCheckMessage": "Indexadores no disponibles debido a errores: {indexerNames}", "Indexer": "Indexador", - "Grabbed": "Añadido", + "Grabbed": "Capturado", "GeneralSettingsSummary": "Puerto, SSL, nombre de usuario/contraseña , proxy, analíticas, y actualizaciones", "Filename": "Nombre de archivo", "Failed": "Fallido", @@ -99,13 +99,13 @@ "Peers": "Pares", "PageSize": "Tamaño de Página", "Ok": "Ok", - "OAuthPopupMessage": "Pop-ups bloqueados por su navegador", + "OAuthPopupMessage": "Los elementos emergentes están siendo bloqueados por tu navegador", "Name": "Nombre", "Message": "Mensaje", "Level": "Nivel", "KeyboardShortcuts": "Atajos de Teclado", "Info": "Info", - "HealthNoIssues": "No hay problemas con tu configuración", + "NoIssuesWithYourConfiguration": "No hay problemas con tu configuración", "Error": "Error", "ConnectionLost": "Conexión perdida", "Component": "Componente", @@ -114,7 +114,7 @@ "Cancel": "Cancelar", "Apply": "Aplicar", "Age": "Antigüedad", - "SystemTimeCheckMessage": "El reloj del sistema está retrasado más de un día. Las tareas de mantenimiento no se ejecutarán correctamente hasta que se haya corregido", + "SystemTimeHealthCheckMessage": "El reloj del sistema está retrasado más de un día. Las tareas de mantenimiento no se ejecutarán correctamente hasta que se haya corregido", "UnsavedChanges": "Cambios sin guardar", "ShowSearchHelpText": "Muestra el botón de búsqueda al pasar por encima", "ShowSearch": "Mostrar búsqueda", @@ -135,7 +135,7 @@ "DownloadClientSettings": "Opciones del cliente de descarga", "Docker": "Docker", "DeleteTag": "Eliminar Etiqueta", - "DeleteNotification": "Borrar Notificacion", + "DeleteNotification": "Eliminar Notificación", "DeleteDownloadClient": "Borrar cliente de descarga", "DeleteBackup": "Eliminar copia de seguridad", "DatabaseMigration": "Migración de la base de datos", @@ -143,7 +143,7 @@ "ClientPriority": "Prioridad del Cliente", "ChangeHasNotBeenSavedYet": "El cambio aún no se ha guardado", "CertificateValidationHelpText": "Cambiar la rigidez de la validación de la certificación HTTPS", - "CertificateValidation": "Validacion de certificado", + "CertificateValidation": "Validación de certificado", "BypassProxyForLocalAddresses": "Omitir Proxy para Direcciones Locales", "Branch": "Rama", "BindAddressHelpText": "Dirección IP4 válida, localhost o '*' para todas las interfaces", @@ -167,11 +167,11 @@ "URLBase": "URL base", "Uptime": "Tiempo de actividad", "UpdateScriptPathHelpText": "Ruta a un script personalizado que toma un paquete de actualización extraído y gestiona el resto del proceso de actualización", - "UpdateMechanismHelpText": "Usar el actualizador integrado de {appName} o un script", + "UpdateMechanismHelpText": "Usa el actualizador integrado de {appName} o un script", "UpdateAutomaticallyHelpText": "Descargar e instalar actualizaciones automáticamente. Todavía puedes instalar desde Sistema: Actualizaciones", "UnableToLoadTags": "No se pueden cargar las Etiquetas", "UnableToLoadNotifications": "No se pueden cargar las Notificaciones", - "DownloadClientsLoadError": "No se puden cargar los gestores de descargas", + "DownloadClientsLoadError": "No se pudieron cargar los clientes de descargas", "UISettings": "Ajustes del UI", "Torrents": "Torrents", "TestAllClients": "Probar todos los clientes", @@ -212,7 +212,7 @@ "Mechanism": "Mecanismo", "Logs": "Registros", "LogLevel": "Nivel de Registro", - "LaunchBrowserHelpText": " Abrir un navegador web e ir a la página de inicio de {appName} al arrancar la app.", + "LaunchBrowserHelpText": " Abre un navegador web y navega a la página de inicio de {appName} al iniciarse la aplicación.", "Interval": "Intervalo", "IndexerFlags": "Indicadores del indexador", "IncludeHealthWarningsHelpText": "Incluir Alertas de Salud", @@ -236,19 +236,19 @@ "ExistingTag": "Etiquetas existentes", "EnableInteractiveSearchHelpText": "Se usará cuando se utilice la búsqueda interactiva", "EnableAutomaticSearchHelpText": "Será usado cuando las búsquedas automáticas sean realizadas por la interfaz de usuario o por {appName}", - "DeleteTagMessageText": "¿Está seguro de querer eliminar la etiqueta '{label}'?", - "DeleteNotificationMessageText": "¿Seguro que quieres eliminar la notificación '{name}'?", - "DeleteBackupMessageText": "Seguro que quieres eliminar la copia de seguridad '{name}'?", - "DeleteDownloadClientMessageText": "Seguro que quieres eliminar el gestor de descargas '{name}'?", - "CancelPendingTask": "Estas seguro de que deseas cancelar esta tarea pendiente?", - "BranchUpdateMechanism": "La rama se uso por un mecanisco de actualizacion externo", + "DeleteTagMessageText": "¿Estás seguro que quieres eliminar la etiqueta '{label}'?", + "DeleteNotificationMessageText": "¿Estás seguro que quieres eliminar la notificación '{name}'?", + "DeleteBackupMessageText": "¿Estás seguro que quieres eliminar la copia de seguridad '{name}'?", + "DeleteDownloadClientMessageText": "¿Estás seguro que quieres eliminar el cliente de descarga '{name}'?", + "CancelPendingTask": "¿Estás seguro que quieres cancelar esta tarea pendiente?", + "BranchUpdateMechanism": "Rama usada por un mecanismo de actualización externo", "BranchUpdate": "Rama a usar para actualizar {appName}", "BeforeUpdate": "Antes de actualizar", "AddingTag": "Añadir etiqueta", "UnableToLoadUISettings": "No se han podido cargar los ajustes de UI", "UnableToLoadHistory": "No se ha podido cargar la historia", "UnableToLoadGeneralSettings": "No se han podido cargar los ajustes Generales", - "UnableToLoadBackups": "No se pudo cargar las copias de seguridad", + "BackupsLoadError": "No se pudo cargar las copias de seguridad", "UnableToAddANewNotificationPleaseTryAgain": "No se ha podido añadir una nueva notificación, prueba otra vez.", "UnableToAddANewIndexerPleaseTryAgain": "No se pudo añadir un nuevo indexador, por favor inténtalo de nuevo.", "UnableToAddANewDownloadClientPleaseTryAgain": "No se ha podido añadir un nuevo gestor de descargas, prueba otra vez.", @@ -261,7 +261,7 @@ "NoTagsHaveBeenAddedYet": "Ninguna etiqueta ha sido añadida aún", "NoLogFiles": "No hay archivos de registro", "NoBackupsAreAvailable": "No hay copias de seguridad disponibles", - "MaintenanceRelease": "Lanzamiento de mantenimiento: Corrección de errores y otras mejoras. Ver historial de commits de Github para mas detalle", + "MaintenanceRelease": "Lanzamiento de mantenimiento: Corrección de errores y otras mejoras. Ver el historial de commits de Github para más detalles", "ForMoreInformationOnTheIndividualDownloadClients": "Para más información individual de los gestores de descarga, haz clic en lls botones de información.", "FilterPlaceHolder": "Buscar Indexadores", "Exception": "Excepción", @@ -280,8 +280,8 @@ "FocusSearchBox": "Enfocar Campo de Búsqueda", "SaveSettings": "Guardar ajustes", "OpenThisModal": "Abrir esta Ventana Modal", - "MovieIndexScrollTop": "Indice de Películas: Desplazar hacia arriba", - "MovieIndexScrollBottom": "Indice de Películas: Desplazar hacia abajo", + "MovieIndexScrollTop": "Índice de Películas: Desplazar hacia arriba", + "MovieIndexScrollBottom": "Índice de Películas: Desplazar hacia abajo", "CloseCurrentModal": "Cerrar esta Ventana Modal", "AcceptConfirmationModal": "Aceptar Confirmación de esta Ventana Modal", "IndexerLongTermStatusUnavailableHealthCheckMessage": "Indexadores no disponibles debido a errores durante más de 6 horas: {indexerNames}", @@ -295,7 +295,7 @@ "Donations": "Donaciones", "SearchIndexers": "Buscar Indexadores", "Enabled": "Habilitado", - "Grabs": "Capturar", + "Grabs": "Capturas", "Presets": "Preajustes", "Rss": "RSS", "Today": "Hoy", @@ -363,7 +363,7 @@ "Started": "Iniciado", "Remove": "Eliminar", "Replace": "Reemplazar", - "TheLatestVersionIsAlreadyInstalled": "La última versión de {appName} ya está instalada", + "OnLatestVersion": "La última versión de {appName} ya está instalada", "Apps": "Aplicaciones", "AddApplication": "Añadir aplicación", "AddCustomFilter": "Añadir Filtro Personalizado", @@ -388,7 +388,7 @@ "More": "Más", "Track": "Pista", "Year": "Año", - "UpdateAvailableHealthCheckMessage": "La nueva actualización está disponible", + "UpdateAvailableHealthCheckMessage": "Una nueva actualización está disponible: {version}", "Genre": "Género", "Publisher": "Editor", "AuthenticationRequired": "Autenticación requerida", @@ -398,7 +398,7 @@ "EditSelectedDownloadClients": "Editar Clientes de Descarga Seleccionados", "EditSelectedIndexers": "Editar Indexadores Seleccionados", "Implementation": "Implementación", - "ManageDownloadClients": "Gestionar Clientes de Descarga", + "ManageDownloadClients": "Administrar Clientes de Descarga", "ApiKeyValidationHealthCheckMessage": "Actualice su clave de API para que tenga al menos {length} carácteres. Puede hacerlo en los ajustes o en el archivo de configuración", "IndexerDownloadClientHealthCheckMessage": "Indexadores con clientes de descarga inválidos: {indexerNames}.", "Episode": "Episodio", @@ -425,7 +425,7 @@ "ResetAPIKeyMessageText": "¿Estás seguro que quieres restablecer tu clave API?", "EditIndexerProxyImplementation": "Editar proxy de indexador - {implementationName}", "AppUpdated": "{appName} Actualizado", - "AppUpdatedVersion": "{appName} ha sido actualizado a la versión `{version}`, para obtener los cambios más recientes, necesitará recargar {appName}", + "AppUpdatedVersion": "{appName} ha sido actualizado a la versión `{version}`, para obtener los cambios más recientes tendrás que recargar {appName}", "AddApplicationImplementation": "Agregar aplicación - {implementationName}", "AddConnectionImplementation": "Añadir Conexión - {implementationName}", "AddIndexerImplementation": "Agregar Indexador - {implementationName}", @@ -493,9 +493,9 @@ "EditCategory": "Editar categoría", "EditSyncProfile": "Editar perfil de sincronización", "EnableIndexer": "Habilitar indexador", - "InvalidUILanguage": "Su interfaz de usuario está configurada en un idioma no válido, corríjalo y guarde la configuración", + "InvalidUILanguage": "Tu interfaz de usuario está configurada en un idioma inválido, corrígelo y guarda la configuración", "DownloadClientQbittorrentSettingsContentLayout": "Diseño del contenido", - "DownloadClientQbittorrentSettingsContentLayoutHelpText": "Si usar el diseño de contenido configurado de qBittorrent, el diseño original del torrent o siempre crear una subcarpeta (qBittorrent 4.3.2+)", + "DownloadClientQbittorrentSettingsContentLayoutHelpText": "Si usa el diseño de contenido configurado de qBittorrent, el diseño original del torrent o siempre crea una subcarpeta (qBittorrent 4.3.2+)", "EnableRssHelpText": "Habilitar canal RSS para el Indexador", "days": "días", "ElapsedTime": "Tiempo transcurrido", @@ -517,7 +517,7 @@ "IndexerTagsHelpTextWarning": "Las etiquetas deben utilizarse con precaución, ya que pueden tener efectos no deseados. Un indexador con una etiqueta solo se sincronizará con aplicaciones que tengan la misma etiqueta.", "TVSearchTypes": "Tipos de búsqueda de TV", "DownloadClientAriaSettingsDirectoryHelpText": "Ubicación opcional en la que poner las descargas, dejar en blanco para usar la ubicación de Aria2 predeterminada", - "IndexerNoDefCheckMessage": "Los indexadores no tienen definición y no funcionarán: {0}. Por favor elimínelos y (o) vuelva a añadirlos a {appName}", + "IndexerNoDefinitionCheckHealthCheckMessage": "Los indexadores no tienen definición y no funcionarán: {indexerNames}. Por favor elimínalos y (o) vuelve a añadirlos a {appName}.", "IndexerProxy": "Proxy del Indexador", "IndexerObsoleteCheckMessage": "Los indexadores están obsoletos o se han actualizado: {0}. Por favor elimínelos y (o) vuelva a añadirlos a {appName}", "IncludeManualGrabsHelpText": "Incluir las Capturas Manuales realizadas en {appName}", @@ -537,7 +537,7 @@ "IndexerTagsHelpText": "Utilice etiquetas para especificar los Proxies del Indexador o las aplicaciones con las que se sincroniza el indexador.", "MassEditor": "Editor Masivo", "ApplicationsLoadError": "No se puede cargar la lista de aplicaciones", - "ManageClients": "Gestionar Clientes", + "ManageClients": "Administrar Clientes", "ManageApplications": "Gestionar Aplicaciones", "IndexerId": "ID del Indexador", "IndexerInfo": "Información del Indexador", @@ -609,10 +609,10 @@ "Url": "Url", "VipExpiration": "Expiración VIP", "TotalIndexerSuccessfulGrabs": "Capturas con Éxito Totales por Indexador", - "NotificationsEmailSettingsUseEncryptionHelpText": "Si prefiere utilizar el cifrado si está configurado en el servidor, utilizar siempre el cifrado mediante SSL (sólo puerto 465) o StartTLS (cualquier otro puerto) o no utilizar nunca el cifrado", + "NotificationsEmailSettingsUseEncryptionHelpText": "Si prefiere utilizar el cifrado si está configurado en el servidor, utilizar siempre el cifrado mediante SSL (sólo puerto 465) o StartTLS (cualquier otro puerto), o no utilizar nunca el cifrado", "IndexerHDBitsSettingsPasskeyHelpText": "Clave de acceso desde los Detalles de Usuario", "IndexerSettingsPasskey": "Clave de Acceso", - "BlackholeFolderHelpText": "La carpeta en donde {appName} se almacenaran los {extension} file", + "BlackholeFolderHelpText": "La carpeta donde {appName} almacenará los archivos {extension}", "CustomFilter": "Filtro personalizado", "LabelIsRequired": "Se requiere etiqueta", "TorrentBlackholeSaveMagnetFiles": "Guardar archivos magnet", @@ -645,7 +645,7 @@ "DownloadClientRTorrentSettingsUrlPath": "Ruta de url", "IndexerHDBitsSettingsOrigins": "Orígenes", "IndexerIPTorrentsSettingsCookieUserAgentHelpText": "User-Agent asociado con la cookie usada desde el navegador", - "IndexerSettingsSeedRatioHelpText": "El ratio que un torrent debería alcanzar antes de detenerse, vacío usa el predeterminado del cliente de descarga. El ratio debería ser al menos 1.0 y seguir las reglas de los indexadores", + "IndexerSettingsSeedRatioHelpText": "El ratio que un torrent debería alcanzar antes de parar, vacío usa el predeterminado del cliente de descarga. El ratio debería ser al menos 1.0 y seguir las reglas de los indexadores", "ApplicationSettingsSyncRejectBlocklistedTorrentHashes": "Sincronizar rechazo de hashes de torrents en la lista de bloqueo mientras se captura", "IndexerGazelleGamesSettingsApiKeyHelpTextWarning": "Debes tener permisos de usuario y de torrents", "IndexerGazelleGamesSettingsSearchGroupNamesHelpText": "Busca lanzamientos por nombres de grupo", @@ -679,7 +679,7 @@ "IndexerSettingsCookie": "Cookie", "IndexerSettingsRssKey": "Clave RSS", "IndexerSettingsSeedRatio": "Ratio de sembrado", - "IndexerSettingsSeedTimeHelpText": "El tiempo que un torrent debería ser compartido antes de detenerse, vació usa el predeterminado del cliente de descarga", + "IndexerSettingsSeedTimeHelpText": "El tiempo que un torrent debería ser sembrado antes de parar, vacío usa el predeterminado del cliente de descarga", "IndexerMTeamTpSettingsApiKeyHelpText": "Clave API del sitio (encontrada en Panel de control de usuario => Seguridad => Laboratorio)", "IndexerMTeamTpSettingsFreeleechOnlyHelpText": "Buscar solo lanzamientos freeleech", "Menu": "Menú", @@ -760,5 +760,52 @@ "OverrideGrabModalTitle": "Sobrescribir y capturar - {title}", "PrioritySettings": "Prioridad: {priority}", "SelectDownloadClientModalTitle": "{modalTitle} - Seleccionar cliente de descarga", - "Default": "Predeterminado" + "Default": "Predeterminado", + "BuiltIn": "Integrado", + "Script": "Script", + "Any": "Cualquiera", + "Redirected": "Redirección", + "InfoUrl": "Información de la URL", + "PublishedDate": "Fecha de publicación", + "AverageQueries": "Promedio de peticiones", + "AverageGrabs": "Promedio de capturas", + "AllSearchResultsHiddenByFilter": "Todos los resultados están ocultos por el filtro aplicado.", + "PackageVersionInfo": "{packageVersion} por {packageAuthor}", + "HealthMessagesInfoBox": "Puedes encontrar más información sobre la causa de estos mensajes de comprobación de salud haciendo clic en el enlace wiki (icono de libro) al final de la fila, o comprobando tus [registros]({link}). Si tienes dificultades para interpretar estos mensajes, puedes ponerte en contacto con nuestro soporte en los enlaces que aparecen abajo.", + "LogSizeLimit": "Límite de tamaño de registro", + "LogSizeLimitHelpText": "Máximo tamaño de archivo de registro en MB antes de archivarlo. Predeterminado es 1MB.", + "PreferMagnetUrl": "Preferir URL magnet", + "IndexerSettingsPreferMagnetUrl": "Preferir URL magnet", + "IndexerSettingsPreferMagnetUrlHelpText": "Cuando está habilitado, este indexador preferirá el uso de URL magnet para capturas con alternativas a enlaces torrent", + "PreferMagnetUrlHelpText": "Cuando está habilitado, este indexador preferirá el uso de URL magnet para capturas con alternativas a enlaces torrent", + "IndexerPassThePopcornSettingsGoldenPopcornOnly": "Solo Golden Popcorn", + "IndexerPassThePopcornSettingsGoldenPopcornOnlyHelpText": "Busca lanzamientos solo en Golden Popcorn", + "IndexerAvistazSettingsFreeleechOnlyHelpText": "Buscar solo lanzamientos freeleech", + "IndexerAvistazSettingsUsernameHelpText": "Nombre de usuario del sitio", + "IndexerAvistazSettingsUsernameHelpTextWarning": "Solo los miembros de rango y superiores pueden usar la API en este indexador.", + "IndexerAvistazSettingsPasswordHelpText": "Contraseña del sitio", + "IndexerAvistazSettingsPidHelpText": "PID de la página de Mi cuenta o Mi perfil", + "LogFilesLocation": "Los archivos de registro se encuentran en: {location}", + "DockerUpdater": "Actualiza el contenedor docker para recibir la actualización", + "Download": "Descargar", + "ErrorRestoringBackup": "Error restaurando la copia de seguridad", + "ExternalUpdater": "{appName} está configurado para usar un mecanismo de actualización externo", + "FailedToFetchUpdates": "Fallo al buscar las actualizaciones", + "Logout": "Cerrar Sesión", + "NoEventsFound": "Ningún evento encontrado", + "RestartReloadNote": "Nota: {appName} se reiniciará automáticamente y recargará la interfaz durante el proceso de restauración.", + "TheLogLevelDefault": "El nivel de registro por defecto es 'Info' y puede ser cambiado en [Opciones generales](opciones/general)", + "UpdateAppDirectlyLoadError": "No se pudo actualizar {appName} directamente,", + "UpdaterLogFiles": "Actualizador de archivos de registro", + "WouldYouLikeToRestoreBackup": "Te gustaria restaurar la copia de seguridad '{name}'?", + "AptUpdater": "Usa apt para instalar la actualización", + "Install": "Instalar", + "InstallLatest": "Instala el último", + "InstallMajorVersionUpdateMessage": "Esta actualización instalará una nueva versión principal y podría no ser compatible con tu sistema. ¿Estás seguro que quieres instalar esta actualización?", + "InstallMajorVersionUpdate": "Instalar actualización", + "InstallMajorVersionUpdateMessageLink": "Por favor revisa [{domain}]({url}) para más información.", + "FailedToFetchSettings": "Error al recuperar la configuración", + "CurrentlyInstalled": "Actualmente instalado", + "PreviouslyInstalled": "Previamente instalado", + "DownloadClientUTorrentProviderMessage": "uTorrent tiene un amplio historial de incluir criptomineros, malware y publicidad, por lo que recomendamos encarecidamente que elijas un cliente diferente." } diff --git a/src/NzbDrone.Core/Localization/Core/fa.json b/src/NzbDrone.Core/Localization/Core/fa.json index 0967ef424..c0f9f6513 100644 --- a/src/NzbDrone.Core/Localization/Core/fa.json +++ b/src/NzbDrone.Core/Localization/Core/fa.json @@ -1 +1,13 @@ -{} +{ + "ApiKey": "کلید API", + "NetCore": ".NET", + "Add": "افزودن", + "About": "درباره", + "Actions": "اقدامات", + "Docker": "Docker", + "AddConnection": "افزودن پیوند", + "AddConnectionImplementation": "افزودن پیوند - {implementationName}", + "AddDownloadClientImplementation": "افزودن کلاینت دانلود - {implementationName}", + "Torrents": "تورنت ها", + "Usenet": "Usenet" +} diff --git a/src/NzbDrone.Core/Localization/Core/fi.json b/src/NzbDrone.Core/Localization/Core/fi.json index 63dd34ebf..0236ce555 100644 --- a/src/NzbDrone.Core/Localization/Core/fi.json +++ b/src/NzbDrone.Core/Localization/Core/fi.json @@ -4,12 +4,12 @@ "LogLevel": "Lokikirjauksen laajuus", "MovieIndexScrollTop": "Elokuvakirjasto: vieritä ylös", "Apply": "Käytä", - "ClientPriority": "Lataustyökalun painotus", - "IndexerPriorityHelpText": "Tietolähteen painotus, 1– 50 (korkein-alin). Oletusarvo on 25. Käytetään muutoin tasaveroisten julkaisujen kaappauspäätökseen. Kaikkia käytössä olevia tietolähteitä käytetään edelleen RSS-synkronointiin ja hakuun.", + "ClientPriority": "Latauspalvelun painotus", + "IndexerPriorityHelpText": "Hakupalvelun painotus, 1– 50 (korkein-alin). Oletusarvo on 25.", "Manual": "Manuaalinen", "Add": "Lisää", "Reload": "Lataa uudelleen", - "Indexers": "Tietolähteet", + "Indexers": "Hakupalvelut", "MovieIndexScrollBottom": "Elokuvakirjasto: vieritä alas", "SSLCertPassword": "SSL-varmenteen salasana", "Style": "Ulkoasu", @@ -25,16 +25,16 @@ "SettingsTimeFormat": "Kellonajan esitys", "Message": "Viesti", "Seeders": "Jakajat", - "TestAll": "Kaikkien testaus", - "AddDownloadClient": "Lisää lataustyökalu", + "TestAll": "Koesta kaikki", + "AddDownloadClient": "Lisää latauspalvelu", "CustomFilters": "Omat suodattimet", "DeleteTag": "Poista tunniste", "EnableRss": "Käytä RSS-syötettä", "Filter": "Suodatus", "Fixed": "Korjattu", "FocusSearchBox": "Kohdista hakukenttä", - "ForMoreInformationOnTheIndividualDownloadClients": "Saat yksittäisestä lataustyökalusta lisätietoja painamalla sen ohessa olevaa \"Lisätietoja\"-painiketta.", - "HideAdvanced": "Piilota lisäasetukset", + "ForMoreInformationOnTheIndividualDownloadClients": "Saat lisätietoja yksittäisistä latauspalveluista painamalla niiden ohessa olevia lisätietopainikkeita.", + "HideAdvanced": "Laajenna asetukset", "History": "Historia", "MIA": "Puuttuu", "New": "Uutta", @@ -45,21 +45,21 @@ "Refresh": "Päivitä", "RefreshMovie": "Päivitä elokuva", "ReleaseBranchCheckOfficialBranchMessage": "\"{0}\" ei ole kelvollinen {appName}-julkaisuhaara ja tämän vuoksi et saa päivityksiä.", - "RestartRequiredHelpTextWarning": "Käyttöönotto vaatii in uudelleenkäynnistyksen.", + "RestartRequiredHelpTextWarning": "Käyttöönotto vaatii sovelluksen uudelleenkäynnistyksen.", "Result": "Tulos", "Settings": "Asetukset", "SettingsLongDateFormat": "Pitkän päiväyksen esitys", "SettingsShortDateFormat": "Lyhyen päiväyksen esitys", "UnselectAll": "Tyhjennä valinnat", "UpdateStartupTranslocationHealthCheckMessage": "Päivitystä ei voida asentaa, koska käynnistyskansio \"{startupFolder}\" sijaitsee \"App Translocation\" -kansiossa.", - "UpdateUiNotWritableHealthCheckMessage": "Päivityksen asennus ei onnistu, koska käyttäjällä \"{userName}\" ei ole kirjoitusoikeutta käyttöliittymäkansioon \"{uiFolder}\".", + "UpdateUiNotWritableHealthCheckMessage": "Päivityksen asennus ei onnistu, koska käyttäjällä {userName} ei ole kirjoitusoikeutta käyttöliittymäkansioon \"{uiFolder}\".", "UpdateMechanismHelpText": "Käytä {appName}in sisäänrakennettua päivitystoimintoa tai komentosarjaa.", "Enable": "Käytä", "UI": "Käyttöliittymä", "Usenet": "Usenet", "BackupNow": "Varmuuskopioi nyt", "NoBackupsAreAvailable": "Varmuuskopioita ei ole käytettävissä", - "UpdateStartupNotWritableHealthCheckMessage": "Päivitystä ei voida asentaa, koska käyttäjällä \"{userName}\" ei ole kirjoitusoikeutta käynnistyskansioon \"{startupFolder}\".", + "UpdateStartupNotWritableHealthCheckMessage": "Päivitystä ei voida asentaa, koska käyttäjällä {userName} ei ole kirjoitusoikeutta käynnistyskansioon \"{startupFolder}\".", "Updates": "Päivitykset", "UpdateScriptPathHelpText": "Polku komentosarjaan, joka käsittelee puretun päivitystiedoston ja hoitaa asennuksen loppuosuuden.", "Uptime": "Käyttöaika", @@ -70,12 +70,12 @@ "NoTagsHaveBeenAddedYet": "Tunnisteita ei ole vielä lisätty.", "ApplyTags": "Tunnistetoimenpide", "Authentication": "Tunnistautuminen", - "AuthenticationMethodHelpText": "Vaadi {appName}in käyttöön käyttäjätunnus ja salasana", + "AuthenticationMethodHelpText": "Vaadi {appName}in käyttöön käyttäjätunnus ja salasana.", "BindAddressHelpText": "Toimiva IP-osoite, localhost tai * (tähti) kaikille verkkoliitännöille.", "Close": "Sulje", - "DeleteNotification": "Poista ilmoitus", + "DeleteNotification": "Poista ilmoituspalvelu", "Docker": "Docker", - "DownloadClient": "Lataustyökalu", + "DownloadClient": "Latauspalvelu", "Language": "Kieli", "Search": "Haku", "Details": "Tiedot", @@ -88,14 +88,14 @@ "Logs": "Lokitiedot", "Mechanism": "Mekanismi", "Name": "Nimi", - "NoLinks": "Ei linkkejä", + "NoLinks": "Kytköksiä ei ole", "Peers": "Vertaiset", "Presets": "Esiasetukset", "Priority": "Painotus", "Protocol": "Protokolla", "ProxyBadRequestHealthCheckMessage": "Välityspalvelintesti epäonnistui. Tilakoodi: {statusCode}.", "ProxyFailedToTestHealthCheckMessage": "Välityspalvelintesti epäonnistui: {url}", - "ProxyResolveIpHealthCheckMessage": "Määritetyn välityspalvelimen \"{0}\" IP-osoitteen selvitys epäonnistui.", + "ProxyResolveIpHealthCheckMessage": "Määritetyn välityspalvelimen \"{proxyHostName}\" IP-osoitteen selvitys epäonnistui.", "ProxyPasswordHelpText": "Käyttäjätunnus ja salasana tulee täyttää vain tarvittaessa. Mikäli näitä ei ole, tulee kentät jättää tyhjiksi.", "ProxyType": "Välityspalvelimen tyyppi", "ProxyUsernameHelpText": "Käyttäjätunnus ja salasana tulee täyttää vain tarvittaessa. Mikäli näitä ei ole, tulee kentät jättää tyhjiksi.", @@ -111,18 +111,18 @@ "RestartNow": "Käynnistä uudelleen nyt", "Restore": "Palauta", "Rss": "RSS", - "RssIsNotSupportedWithThisIndexer": "RSS-syötettä ei ole käytettävissä tälle tietolähteelle", + "RssIsNotSupportedWithThisIndexer": "Tämän hakupalvelun kanssa ei voida käyttää RSS-syötettä.", "ScriptPath": "Komentosarjan sijainti", "Security": "Suojaus", "SuggestTranslationChange": "Ehdota käännösmuutosta", "System": "Järjestelmä", - "SystemTimeCheckMessage": "Järjestelmän ajassa on ainakin vuorokauden heitto eivätkä ajoitetut tehtävät tämän vuoksi toimi oikein ennen kuin se on korjattu.", - "TagCannotBeDeletedWhileInUse": "Tunnistetta ei voi poistaa, koska se on käytössä", - "TagIsNotUsedAndCanBeDeleted": "Tunnistetta ei ole määritetty millekään kohteelle, joten sen voi poistaa.", + "SystemTimeHealthCheckMessage": "Järjestelmän aika on ainakin vuorokauden pielessä, eivätkä ajoitetut tehtävät toimi oikein ennen kuin se on korjattu.", + "TagCannotBeDeletedWhileInUse": "Tunnistetta ei voida poistaa kun se on käytössä.", + "TagIsNotUsedAndCanBeDeleted": "Tunniste ei ole käytössä ja voidaan poistaa.", "TagsSettingsSummary": "Täältä näet kaikki tunnisteet käyttökohteineen ja voit poistaa käyttämättömät tunnisteet.", "Tasks": "Tehtävät", - "Test": "Testaa", - "TestAllClients": "Lataustyökalujen testaus", + "Test": "Koesta", + "TestAllClients": "Koesta palvelut", "Time": "Aika", "Title": "Nimike", "Tomorrow": "Huomenna", @@ -130,52 +130,52 @@ "Torrents": "Torrentit", "Type": "Tyyppi", "UILanguage": "Käyttöliittymän kieli", - "UnableToAddANewApplicationPleaseTryAgain": "Uuden sovelluksen lisäys epäonnistui. Yritä uudelleen.", - "UnableToAddANewIndexerPleaseTryAgain": "Uuden tietolähteen lisäys epäonnistui. Yritä uudelleen.", - "UnableToAddANewIndexerProxyPleaseTryAgain": "Uuden tiedonhaun välityspalvelimen lisäys epäonnistui. Yritä uudelleen.", - "UnableToLoadBackups": "Varmuuskopioiden lataus epäonnistui", - "DownloadClientsLoadError": "Lataustyökalujen lataus ei onistu", - "UnableToLoadGeneralSettings": "Virhe ladattaessa yleisiä asetuksia", + "UnableToAddANewApplicationPleaseTryAgain": "Virhe lisättäessä sovellusta. Yritä uudelleen.", + "UnableToAddANewIndexerPleaseTryAgain": "Uuden hakupalvelun lisääminen epäonnistui. Yritä uudelleen.", + "UnableToAddANewIndexerProxyPleaseTryAgain": "Virhe lisättäessä tiedonhaun välityspalvelinta. Yritä uudelleen.", + "BackupsLoadError": "Virhe ladattaessa varmuuskopioita.", + "DownloadClientsLoadError": "Virhe ladattaessa latauspalveluita.", + "UnableToLoadGeneralSettings": "Yleisasetusten lataus epäonnistui", "UpdateAutomaticallyHelpText": "Lataa ja asenna päivitykset automaattisesti. Voit myös edelleen suorittaa asennuksen järjestelmäasetusten päivitykset-osiosta.", "Added": "Lisäysaika", - "AddIndexer": "Lisää tietolähde", + "AddIndexer": "Lisää hakupalvelu", "AddingTag": "Tunniste lisätään", "Age": "Ikä", "All": "Kaikki", - "AllIndexersHiddenDueToFilter": "Aktiivinen suodatin on piilottanut kaikki tietolähteet.", + "AllIndexersHiddenDueToFilter": "Aktiivinen suodatin on piilottanut kaikki hakupalvelut.", "Analytics": "Analytiikka", "AnalyticsEnabledHelpText": "Lähetä nimettömiä käyttö- ja virhetietoja {appName}in palvelimille. Tämä sisältää tietoja selaimestasi, käyttöliittymän sivujen käytöstä, virheraportoinnista, käyttöjärjestelmästä ja suoritusalustasta. Käytämme näitä tietoja ominaisuuksien ja vikakorjausten painotukseen.", "ApiKey": "Rajapinnan avain", "AppDataDirectory": "AppData-kansio", "DatabaseMigration": "Tietokannan siirto", "Delete": "Poista", - "DeleteIndexerProxyMessageText": "Haluatko varmasti poistaa tietolähdevälityspalvelimen \"{name}\"?", + "DeleteIndexerProxyMessageText": "Haluatko varmasti poistaa hakupalveluvälityspalvelimen \"{name}\"?", "DeleteNotificationMessageText": "Haluatko varmasti poistaa ilmoituspalvelun \"{name}\"?", "Disabled": "Ei käytössä", - "DownloadClients": "Lataustyökalut", - "DownloadClientSettings": "Lataustyökalujen asetukset", - "DownloadClientStatusAllClientHealthCheckMessage": "Lataustyökaluja ei ole ongelmien vuoksi käytettävissä", + "DownloadClients": "Latauspalvelut", + "DownloadClientSettings": "Latauspalveluasetukset", + "DownloadClientStatusAllClientHealthCheckMessage": "Latauspalveluita ei ole ongelmien vuoksi käytettävissä", "Mode": "Tila", "MoreInfo": "Lisätietoja", "SelectAll": "Valitse kaikki", "SendAnonymousUsageData": "Lähetä nimettömiä käyttötietoja", "SetTags": "Tunnisteiden määritys", "SettingsEnableColorImpairedMode": "Heikentyneen värinäön tila", - "ShowAdvanced": "Näytä lisäasetukset", + "ShowAdvanced": "Supista asetukset", "ShowSearchHelpText": "Näytä hakupainike osoitettaessa.", "Shutdown": "Sammuta", "Size": "Koko", "Sort": "Järjestys", - "UnableToAddANewDownloadClientPleaseTryAgain": "Uuden lataustyökalun lisäys epäonnistui. Yitä uudelleen.", + "UnableToAddANewDownloadClientPleaseTryAgain": "Latauspalvelun lisääminen epäonnistui. Yritä uudelleen.", "AppDataLocationHealthCheckMessage": "Päivityksiä ei sallita, jotta AppData-kansion poistaminen päivityksen yhteydessä voidaan estää", - "UnableToLoadHistory": "Historian lataus epäonnistui.", - "UnableToLoadNotifications": "Virhe ladattaessa kytköksiä", - "UnableToLoadTags": "Tunnisteiden lataus ei onnistu", - "UnableToLoadUISettings": "Virhe ladattaesssa käyttöliittymän asetuksia", + "UnableToLoadHistory": "Virhe ladattaessa historiaa.", + "UnableToLoadNotifications": "Virhe ladattaessa ilmoituspalveluita.", + "UnableToLoadTags": "Virhe ladattaessa tunnisteita.", + "UnableToLoadUISettings": "Virhe ladattaessa käyttöliittymäasetuksia.", "UnsavedChanges": "Muutoksia ei ole tallennettu", "Yesterday": "Eilen", - "ConnectionLost": "Ei yhteyttä", - "DeleteDownloadClientMessageText": "Haluatko varmasti poistaa lataustyökalun \"{name}\"?", + "ConnectionLost": "Yhteys menetettiin", + "DeleteDownloadClientMessageText": "Haluatko varmasti poistaa latauspalvelun \"{name}\"?", "DeleteTagMessageText": "Haluatko varmasti poistaa tunnisteen \"{label}\"?", "Discord": "Discord", "Donations": "Lahjoitukset", @@ -202,18 +202,18 @@ "SettingsShowRelativeDates": "Suhteellisten päiväysten esitys", "SettingsShowRelativeDatesHelpText": "Korvaa absoluuttiset päiväykset suhteellisilla päiväyksillä (tänään/eilen/yms.).", "ShowSearch": "Näytä haku", - "Source": "Lähdekoodi", + "Source": "Lähde", "SSLPort": "SSL-portti", "StartTypingOrSelectAPathBelow": "Aloita kirjoitus tai valitse sijainti alta", "StartupDirectory": "Käynnistyskansio", "TableOptions": "Taulukkonäkymän asetukset", - "TableOptionsColumnsMessage": "Valitse näytettävät sarakkeet ja niiden järjestys", - "TagsHelpText": "Käytetään vähintään yhdellä täsmäävällä tunnisteella merkityille tietolähteille. Käytä kaikille jättämällä tyhjäksi.", - "UnableToAddANewAppProfilePleaseTryAgain": "Uuden sovellusprofiilin lisäys epäonnistui. Yritä uudelleen.", - "UnableToAddANewNotificationPleaseTryAgain": "Kytköksen lisäys epäonnistui. Yritä uudelleen.", + "TableOptionsColumnsMessage": "Valitse näytettävät sarakkeet ja niiden järjestys.", + "TagsHelpText": "Käytetään vähintään yhdellä täsmäävällä tunnisteella merkityille hakupalveluille.", + "UnableToAddANewAppProfilePleaseTryAgain": "Virhe lisättäessä sovellusprofiilia. Yritä uudelleen.", + "UnableToAddANewNotificationPleaseTryAgain": "Ilmoituspalvelun lisääminen epäonnistui. Yritä uudelleen.", "Version": "Versio", "View": "Näkymä", - "Warn": "Varoitus", + "Warn": "Varoita", "Wiki": "Wiki", "Port": "Portti", "Automatic": "Automaattinen", @@ -221,39 +221,39 @@ "Backup": "Varmuuskopiointi", "BackupFolderHelpText": "Suhteelliset tiedostosijainnit ovat {appName}in AppData-kansiossa.", "BackupIntervalHelpText": "Tietokannan ja asetusten automaattisen varmuuskopioinnin ajoitus.", - "BackupRetentionHelpText": "Säilytysjaksoa vanhemmat varmuuskopiot siivotaan automaattisesti.", + "BackupRetentionHelpText": "Säilytysaikaa vanhemmat varmuuskopiot siivotaan automaattisesti.", "Backups": "Varmuuskopiot", "BeforeUpdate": "Ennen päivitystä", "BindAddress": "Sidososoite", "Branch": "Haara", - "BranchUpdate": "{appName}in versiopäivityksiin käytettävä kehityshaara", + "BranchUpdate": "{appName}in versiopäivityksiin käytettävä kehityshaara.", "BranchUpdateMechanism": "Ulkoisen päivitysratkaisun käyttämä kehityshaara.", "BypassProxyForLocalAddresses": "Ohjaa paikalliset osoitteet välityspalvelimen ohi", "Cancel": "Peruuta", - "CancelPendingTask": "Haluatko varmasti perua tämän odottavan tehtävän?", + "CancelPendingTask": "Haluatko varmasti perua odottavan tehtävän?", "CertificateValidation": "Varmenteen vahvistus", - "CertificateValidationHelpText": "Muuta HTTPS-varmennevahvistuksen tarkkuutta. Älä muuta, jollet ymmärrä tähän liittyviä riskejä.", + "CertificateValidationHelpText": "Määritä HTTPS-varmennevahvistuksen tiukkuus. Älä muuta, jos et ymmärrä riskejä.", "ChangeHasNotBeenSavedYet": "Muutosta ei ole vielä tallennettu", "Clear": "Tyhjennä", "CloneProfile": "Monista profiili", "CloseCurrentModal": "Sulje nykyinen ikkuna", "Columns": "Sarakkeet", "Component": "Komponentti", - "Connections": "Yhteydet", - "ConnectSettings": "Kytkösasetukset", - "CouldNotConnectSignalR": "SignalR-kirjastoa ei tavoitettu, eikä käyttöliittymää päivitetä", + "Connections": "Ilmoituspalvelut", + "ConnectSettings": "Ilmoituspavelun asetukset", + "CouldNotConnectSignalR": "SignalR-kirjastoa ei tavoitettu, eikä käyttöliittymä päivity.", "Custom": "Mukautettu", "DeleteApplicationMessageText": "Haluatko varmasti poistaa sovelluksen \"{name}\"?", "DeleteBackup": "Poista varmuuskopio", "DeleteBackupMessageText": "Haluatko varmasti poistaa varmuuskopion \"{name}\"?", - "DeleteDownloadClient": "Poista lataustyökalu", - "DownloadClientStatusSingleClientHealthCheckMessage": "Lataustyökaluja ei ole ongelmien vuoksi käytettävissä: {downloadClientNames}", - "EditIndexer": "Muokkaa tietolähdettä", + "DeleteDownloadClient": "Poista latauspalvelu", + "DownloadClientStatusSingleClientHealthCheckMessage": "Latauspalveluita ei ole ongelmien vuoksi käytettävissä: {downloadClientNames}", + "EditIndexer": "Muokkaa hakupalvelua", "EnableAutomaticSearch": "Käytä automaattihakua", "EnableInteractiveSearch": "Käytä manuaalihakuun", "EnableInteractiveSearchHelpText": "Profiilia käytetään manuaalihakuun.", "EnableSSL": "SSL-salaus", - "EnableSslHelpText": " Käyttöönotto vaatii uudelleenkäynnistyksen järjestelmänvavojan oikeuksilla.", + "EnableSslHelpText": " Käyttöönotto vaatii uudelleenkäynnistyksen järjestelmänvalvojan oikeuksilla.", "Error": "Virhe", "ErrorLoadingContents": "Virhe ladattaessa sisältöjä", "Events": "Tapahtumat", @@ -268,26 +268,26 @@ "Grabs": "Kaappaukset", "Health": "Terveys", "Level": "Taso", - "HealthNoIssues": "Kokoonpanossasi ei ole ongelmia", + "NoIssuesWithYourConfiguration": "Kokoonpanossasi ei ole ongelmia.", "HomePage": "Verkkosivusto", "Host": "Osoite", "Hostname": "Osoite", "IncludeHealthWarningsHelpText": "Sisällytä kuntovaroitukset", - "Indexer": "Tietolähde", - "IndexerFlags": "Tietolähteen liput", - "IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Mikään tietolähde ei ole käytettävissä yli 6 tuntia kestäneiden virheiden vuoksi.", - "IndexerLongTermStatusUnavailableHealthCheckMessage": "Tietolähteet eivät ole käytettävissä yli 6 tuntia kestäneiden virheiden vuoksi: {indexerNames}", - "IndexerPriority": "Tietolähteiden painotus", + "Indexer": "Hakupalvelu", + "IndexerFlags": "Hakupalvelun liput", + "IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Mikään hakupalvelu ei ole käytettävissä yli kuusi tuntia kestäneiden virheiden vuoksi.", + "IndexerLongTermStatusUnavailableHealthCheckMessage": "Hakupalvelut eivät ole käytettävissä yli kuusi tuntia kestäneiden virheiden vuoksi: {indexerNames}.", + "IndexerPriority": "Hakupalveluiden painotus", "IndexerProxyStatusAllUnavailableHealthCheckMessage": "Välityspalvelimet eivät ole käytettävissä virheiden vuoksi", - "IndexerStatusAllUnavailableHealthCheckMessage": "Tietolähteet eivät ole käytettävissä virheiden vuoksi", - "IndexerStatusUnavailableHealthCheckMessage": "Tietolähteet eivät ole käytettävissä virheiden vuoksi: {indexerNames}", + "IndexerStatusAllUnavailableHealthCheckMessage": "Hakupalvelut eivät ole virheiden vuoksi käytettävissä.", + "IndexerStatusUnavailableHealthCheckMessage": "Hakupalvelut eivät ole virheiden vuoksi käytettävissä: {indexerNames}.", "NoChange": "Ei muutosta", "NoLogFiles": "Lokitiedostoja ei ole", "SSLCertPasswordHelpText": "PFX-tiedoston salasana", "SSLCertPath": "SSL-varmenteen sijainti", "SSLCertPathHelpText": "PFX-tiedoston sijainti", "Status": "Tila", - "NotificationTriggers": "Laukaisimet", + "NotificationTriggers": "Ilmoituksen laukaisijat", "NoUpdatesAreAvailable": "Päivityksiä ei ole saatavilla", "OAuthPopupMessage": "Selaimesi estää ponnahdukset", "Ok": "Ok", @@ -303,46 +303,46 @@ "RestoreBackup": "Palauta varmuuskopio", "Retention": "Säilytys", "UILanguageHelpText": "{appName}in käyttöliittymän kieli.", - "UILanguageHelpTextWarning": "Selaimen sivupäivitys vaaditaan", + "UILanguageHelpTextWarning": "Vaatii selaimen sivupäivityksen (F5).", "UISettings": "Käyttöliittymän asetukset", - "DownloadClientsSettingsSummary": "{appName}in käyttöliittymästä suoritettavien hakujen yhteydessä käytettävät lataustyökalumääritykset.", - "ProwlarrSupportsAnyDownloadClient": "{appName} tukee alla listatuja lataustyökaluja.", - "AddDownloadClientToProwlarr": "Lisäämällä lataustyökalun {appName} voi käynnistää lataukset suoraan käyttöliittymästä manuaalisen haun yhteydessä.", - "RedirectHelpText": "Uudelleenohjaa tietolähteeltä saapuvat latauspyynnöt ja välitä kaappaus suoraan välittämättä sitä {appName}in välityksellä.", + "DownloadClientsSettingsSummary": "{appName}in käyttöliittymästä suoritettavien hakujen yhteydessä käytettävät latauspalvelut.", + "ProwlarrSupportsAnyDownloadClient": "{appName} tukee kaikkia alla listatuja latauspalveluita.", + "AddDownloadClientToProwlarr": "Lisäämällä latauspalvelun {appName} voi lähettää julkaisut suoraan käyttöliittymästä manuaalihaun tuloksista.", + "RedirectHelpText": "Uudelleenohjaa hakupalvelulta saapuvat latauspyynnöt ja välitä kaappaus suoraan välittämättä sitä {appName}in kautta.", "FullSync": "Täysi synkronointi", - "SyncLevelFull": "Täysi synkronointi: Pitää sovelluksen tietolähteet täysin synkronoituna. Tietolähteisiin {appName}issa tehdyt muutokset synkronoidaan etäsovelluksen kanssa ja kaikki etäsovelluksessa tehdyt muutokset korvataan seuraavan synkronoinnin yhteydessä.", - "EnableIndexer": "Tietolähteen tila", - "FilterPlaceHolder": "Suodata tietolähteitä", - "IndexerHealthCheckNoIndexers": "Yhtään tietolähdettä ei ole käytössä, eikä {appName} tämän vuoksi löydä tuloksia.", - "IndexerObsoleteCheckMessage": "Tietolähteet ovat poistuneet tai ne ovat muuttuneet: {0}. Poista ja/tai lisää ne {appName}iin uudelleen.", + "SyncLevelFull": "Täysi synkronointi: Pitää sovelluksen hakupalvelut täysin synkronoituna. Hakupalveluihin {appName}issa tehdyt muutokset synkronoidaan etäsovelluksen kanssa ja kaikki etäsovelluksessa tehdyt muutokset korvataan seuraavan synkronoinnin yhteydessä.", + "EnableIndexer": "Ota hakupalvelu käyttöön", + "FilterPlaceHolder": "Suodata palveluita", + "IndexerHealthCheckNoIndexers": "Yhtään hakupalvelua ei ole käytössä, eikä {appName} tämän vuoksi löydä tuloksia.", + "IndexerObsoleteCheckMessage": "Hakupalvelut ovat poistuneet tai ne ovat muuttuneet: {0}. Poista tai lisää ne {appName}iin uudelleen.", "IndexerProxy": "Tiedonhaun välityspalvelin", - "IndexerSettingsSummary": "Määritä useita globaaleita tietolähdeasetuksia, kuten välityspalvelimia.", - "IndexerVipExpiringHealthCheckMessage": "Tietolähteen VIP-edut erääntyvät pian: {indexerNames}", - "ProwlarrSupportsAnyIndexer": "{appName} tukee Newznab- ja Torznab-yhteensopivien tietolähteiden ohella myös useita muita lähteitä vaihtoehdoilla \"Yleinen Newznab\" (Usenetille) ja 'Yleinen Torznab' (torrenteille).", - "SettingsIndexerLogging": "Tehostettu tietolähteiden valvonta", + "IndexerSettingsSummary": "Määritä useita globaaleita hakupalveluasetuksia, kuten välityspalvelimia.", + "IndexerVipExpiringHealthCheckMessage": "Hakupalvelun VIP-edut päättyvät pian: {indexerNames}.", + "ProwlarrSupportsAnyIndexer": "{appName} tukee Newznab- ja Torznab-yhteensopivien hakupalveluiden ohella myös useita muita palveluita vaihtoehdoilla \"Yleinen Newznab\" (Usenetille) ja 'Yleinen Torznab' (torrenteille).", + "SettingsIndexerLogging": "Tehostettu hakupalveluiden valvonta", "AddIndexerProxy": "Lisää tiedonhaun välityspalvelin", "UISettingsSummary": "Kalenterin, päiväyksen ja kellonajan sekä kielen ja heikentyneelle värinäölle sopivan tilan asetukset.", - "SettingsIndexerLoggingHelpText": "Kirjaa tarkempia tietoja tietolähteiden toiminnasta, mukaanlukien vastaukset", + "SettingsIndexerLoggingHelpText": "Kirjaa tarkempia tietoja hakupalveluiden toiminnasta, mukaanlukien vastaukset", "IndexerTagsHelpText": "Tunnisteilla voit kohdistaa tiedonhaun välityspalvelimia ja määrittää mihin sovelluksiin ne synkronoidaan.", - "UnableToLoadAppProfiles": "Sovellusprofiilien lataus epäonnistui", - "AppProfileSelectHelpText": "Sovellusprofiilieilla määritetään tietolähteelle sovellussynkronoinnin yhteydessä aktivoitavat hakutavat (RSS/automaatti/manuaali).", - "IndexerQuery": "Tietolähteen kysely", - "IndexerRss": "Tietolähteen RSS", - "SearchIndexers": "Etsi tietolähteistä", + "UnableToLoadAppProfiles": "Virhe ladattaessa sovellusprofiileja.", + "AppProfileSelectHelpText": "Sovellusprofiilieilla määritetään hakupalvelulle sovellussynkronoinnin yhteydessä aktivoitavat hakutavat (RSS/automaatti/manuaali).", + "IndexerQuery": "Hakupalvelukysely", + "IndexerRss": "Hakupalvelun RSS", + "SearchIndexers": "Etsi hakupalveluista", "AddRemoveOnly": "Ainoastaan lisää/poista", - "IndexerVipExpiredHealthCheckMessage": "Tietolähteen VIP-edut ovat erääntyneet: {indexerNames}", + "IndexerVipExpiredHealthCheckMessage": "Hakupalvelun VIP-edut ovat päättyneet: {indexerNames}.", "MaintenanceRelease": "Huoltojulkaisu: korjauksia ja muita parannuksia. Lue lisää Githubin muutoshistoriasta.", "Query": "Kysely", "Redirect": "Uudelleenohjaus", "RestartProwlarr": "Käynnistä {appName} uudelleen", "SyncLevel": "Synkronoinnin laajuus", - "SyncLevelAddRemove": "Vain lisäys/poisto: Kun {appName}in tietolähteitä lisätään tai poistetaan, päivittyy myös etäsovellus.", - "SyncAppIndexers": "Synkronoi tietolähteet", - "TestAllApps": "Testaa kaikki sovellukset", - "UnableToLoadIndexerProxies": "Tiedonhaun välityspalvelimia ei voitu ladata", - "AddedToDownloadClient": "Julkaisu lisättiin lataustyökaluun", - "AddNewIndexer": "Lisää uusi tietolähde", - "AddToDownloadClient": "Lisää julkaisu lataustyökaluun", + "SyncLevelAddRemove": "Vain lisäys/poisto: Kun {appName}in hakupalveluita lisätään tai poistetaan, päivittyy myös tämä etäsovellus.", + "SyncAppIndexers": "Synkronoi sovelluksiin", + "TestAllApps": "Koesta sovellukset", + "UnableToLoadIndexerProxies": "Virhe ladattaessa tiedonhaun välityspalvelimia.", + "AddedToDownloadClient": "Julkaisu lisättiin latauspalveluun", + "AddNewIndexer": "Lisää uusi hakupalvelu", + "AddToDownloadClient": "Lisää julkaisu latauspalveluun", "NoSearchResultsFound": "Tuloksia ei löytynyt. Yritä uutta hakua alta.", "Notification": "Ilmoitus", "DeleteIndexerProxy": "Poista tiedonhaun välityspalvelin", @@ -351,7 +351,7 @@ "SettingsLogRotate": "Lokitiedostojen kierrätys", "SettingsLogSql": "Kirjaa SQL", "SettingsSqlLoggingHelpText": "Kirjaa kaikki {appName}in SQL-kyselyt", - "ConnectSettingsSummary": "Ilmoitukset, kuten viestintä mediapalvelimille ja soittimille, sekä omat komentosarjat.", + "ConnectSettingsSummary": "Yhteydet ilmoituspalveluihin ja mukautetut komentosarjat.", "DevelopmentSettings": "Kehittäjäasetukset", "Description": "Kuvaus", "Id": "ID", @@ -365,16 +365,16 @@ "Category": "Kategoria", "ClearHistory": "Tyhjennä historia", "ClearHistoryMessageText": "Haluatko varmasti tyhjentää kaiken {appName}-historian?", - "Connect": "Kytkökset", - "EnableRssHelpText": "Käytä tietolähteelle RSS-syötettä.", + "Connect": "Ilmoituspalvelut", + "EnableRssHelpText": "Käytä hakupalvelulle RSS-syötettä.", "DeleteApplication": "Poista sovellus", "DeleteAppProfile": "Poista sovellusprofiili", - "IndexerProxies": "Tiedonhaun välityspalvelimet", - "IndexerAuth": "Tietolähteen todennus", - "Notifications": "Kytkökset", - "NotificationTriggersHelpText": "Valitse tämän ilmoituksen laukaisevat tapahtumat.", + "IndexerProxies": "Hakupalveluiden välityspalvelimet", + "IndexerAuth": "Hakupalvelun todennus", + "Notifications": "Ilmoituspalvelut", + "NotificationTriggersHelpText": "Valitse ilmoituksen laukaisevat tapahtumat.", "Stats": "Tilastot", - "UnableToLoadDevelopmentSettings": "Kehittäjäasetusten lataus epäonnistui", + "UnableToLoadDevelopmentSettings": "Virhe ladattaessa kehittäjäasetuksia.", "AppSettingsSummary": "Sovellukset ja asetukset, joilla määritetään miten {appName} viestii PVR-sovellustesi kanssa.", "Privacy": "Yksityisyys", "NetCore": ".NET", @@ -387,54 +387,54 @@ "Filters": "Suodattimet", "OnGrab": "Kun julkaisu kaapataan", "OnHealthIssue": "Vakausongelmat", - "HistoryCleanupDaysHelpText": "Arvo \"0\" (nolla) poistaa automaattisen tyhjennyksen käytöstä.", + "HistoryCleanupDaysHelpText": "Poista automaattinen tyhjennys käytöstä asettamalla arvoksi 0.", "HistoryCleanupDaysHelpTextWarning": "Tässä määritettyä aikaa vanhemmat tiedostot poistetaan automaattisesti roskakorista pysyvästi.", - "TestAllIndexers": "Tietolähteiden testaus", + "TestAllIndexers": "Koesta palvelut", "UserAgentProvidedByTheAppThatCalledTheAPI": "User-Agent-tiedon ilmoitti rajapinnan kanssa viestinyt sovellus.", "Categories": "Kategoriat", "Database": "Tietokanta", "HistoryCleanup": "Historian siivous", - "IndexerAlreadySetup": "Tietolähteestä on määritetty jo ainakin yksi instanssi", - "IndexerInfo": "Tietolähteen tiedot", - "MassEditor": "Joukkoeditori", + "IndexerAlreadySetup": "Hakupalvelusta on määritetty jo ainakin yksi instanssi.", + "IndexerInfo": "Hakupalvelun tiedot", + "MassEditor": "Massamuokkaus", "OnApplicationUpdate": "Kun sovellus päivitetään", "OnApplicationUpdateHelpText": "Kun sovellus päivitetään", "Proxies": "Välityspalvelimet", "Public": "Julkinen", "SemiPrivate": "Osittain yksityinen", - "ApplicationsLoadError": "Sovelluslistausta ei voitu ladata", + "ApplicationsLoadError": "Virhe ladattaessa sovelluslistaa.", "Url": "URL", "Website": "Verkkosivusto", - "IndexerNoDefCheckMessage": "Tietolähteillä ei ole määritystä, eivätkä ne toimi: {0}. Poista ja/tai lisää {appName}iin uudelleen", + "IndexerNoDefinitionCheckHealthCheckMessage": "Hakupalveluiden määritykset puuttuvat, eivätkä ne toimi: {indexerNames}. Poista tai lisää ne {appName}iin uudelleen.", "Private": "Yksityinen", "QueryResults": "Kyselyn tulokset", "Application": "Sovellus", "GrabReleases": "Kaappaa julkaisu(t)", "Link": "Linkki", - "SearchTypes": "Mitä etsitään", - "UnableToLoadIndexers": "Tietolähteiden lataus epäonnistui", + "SearchTypes": "Etsittävät tyypit", + "UnableToLoadIndexers": "Virhe ladattaessa hakupalveluita.", "Yes": "Kyllä", "MappedDrivesRunningAsService": "Yhdistetyt verkkoasemat eivät ole käytettävissä kun sovellus suoritetaan Windows-palveluna. Saat lisätietoja UKK:sta.", "No": "Ei", - "BookSearchTypes": "Kirjojen hakutyypit", - "IndexerDetails": "Tietolähteen tiedot", - "IndexerName": "Tietolähteen nimi", - "IndexerSite": "Tietolähteen sivusto", - "MovieSearchTypes": "Elokuvien hakutyypit", - "MusicSearchTypes": "Musiikin hakutyypit", + "BookSearchTypes": "Etsittävät kirjatyypit", + "IndexerDetails": "Hakupalvelun tiedot", + "IndexerName": "Hakupalvelun nimi", + "IndexerSite": "Hakupalvelun sivusto", + "MovieSearchTypes": "Etsittävät elokuvatyypit", + "MusicSearchTypes": "Etsittävät musiikkityypit", "NotSupported": "Ei tuettu", "RawSearchSupported": "Raakahaku tuettu", "SearchCapabilities": "Hakuominaisuudet", - "TVSearchTypes": "Televisiosarjojen hakutyypit", + "TVSearchTypes": "Etsittävät sarja-/jaksotyypit", "MinimumSeeders": "Jakajien vähimmäismäärä", - "MinimumSeedersHelpText": "Sovelluksen edellyttämä jakajien vähimmäismäärä tietolähteestä kaappaukseen.", + "MinimumSeedersHelpText": "Sovelluksen edellyttämä hakupalvelusta kaapattavien kohteiden jakajien (seed) vähimmäismäärä.", "SyncProfile": "Synkronointiprofiili", "SyncProfiles": "Synkronointiprofiilit", "AddSyncProfile": "Lisää synkronointiprofiili", "EditSyncProfile": "Muokkaa synkronointiprofiilia", "InstanceName": "Instanssin nimi", "InstanceNameHelpText": "Instanssin nimi välilehdellä ja järjestelmälokissa.", - "ThemeHelpText": "Vaihda sovelluksen käyttöliittymän ulkoasua. \"Automaattinen\" vaihtaa vaalean ja tumman tilan välillä käyttöjärjestelmän teeman mukaan. Innoittanut Theme.Park.", + "ThemeHelpText": "Vaihda sovelluksen käyttöliittymän ulkoasua. \"Automaattinen\" vaihtaa vaalean ja tumman tilan välillä käyttöjärjestelmän teeman mukaan. Innoittanut {inspiredBy}.", "Duration": "Kesto", "ElapsedTime": "Kulunut aika", "EnabledRedirected": "Kulunut, uudelleenohjattu", @@ -449,38 +449,38 @@ "ApplicationLongTermStatusCheckAllClientMessage": "Sovellukset eivät ole käytettävissä yli 6 tuntia kestäneiden virheiden vuoksi.", "ApplicationLongTermStatusCheckSingleClientMessage": "Sovellukset eivät ole käytettävissä yli 6 tuntia kestäneiden virheiden vuoksi: {0}", "AreYouSureYouWantToDeleteCategory": "Haluatko varmasti poistaa kohdistetun kategorian?", - "DeleteClientCategory": "Poista lataustyökalukategoria", - "DownloadClientCategory": "Lataustyökalukategoria", + "DeleteClientCategory": "Poista latauspalvelukategoria", + "DownloadClientCategory": "Latauspalvelukategoria", "MappedCategories": "Kohdistetut kategoriat", "AuthenticationRequired": "Vaadi tunnistautuminen", "Remove": "Poista", "Replace": "Korvaa", - "TheLatestVersionIsAlreadyInstalled": "{appName}in uusin versio on jo asennettu", + "OnLatestVersion": "Uusin {appName}-versio on jo asennettu", "ApplicationURL": "Sovelluksen URL", "ApplicationUrlHelpText": "Tämän sovelluksen ulkoinen URL-osoite, johon sisältyy http(s)://, portti ja URL-perusta.", "Track": "Valvo", - "CountIndexersSelected": "{count} tietolähde(ttä) on valittu", - "DeleteSelectedDownloadClients": "Poista lataustyökalu(t)", + "CountIndexersSelected": "{count} hakupalvelu(a) on valittu", + "DeleteSelectedDownloadClients": "Poista valitut latauspalvelu(t)", "DeleteSelectedApplicationsMessageText": "Haluatko varmasti poistaa {count} valit(un/tua) sovellu(sta/ksen)?", - "DeleteSelectedDownloadClientsMessageText": "Haluatko varmasti poistaa {count} valit(n/tua) lataustyökalu(n/a)?", - "DeleteSelectedIndexersMessageText": "Haluatko varmasti poistaa {count} valit(un/tua) tietoläh(teen/dettä)?", + "DeleteSelectedDownloadClientsMessageText": "Haluatko varmasti poistaa {count} valittua latauspalvelua?", + "DeleteSelectedIndexersMessageText": "Haluatko varmasti poistaa {count} valit(un/tua) hakupalvelu(n/a)?", "Publisher": "Julkasija", - "SelectIndexers": "Valitse tietolähteet", + "SelectIndexers": "Palveluiden monivalinta", "Year": "Vuosi", "Genre": "Lajityyppi", "More": "Lisää", "Season": "Kausi", "ApplyTagsHelpTextAdd": "– \"Lisää\" syötetyt tunnisteet aiempiin tunnisteisiin", - "ApplyTagsHelpTextHowToApplyApplications": "Tunnisteiden käyttäminen valituille sovelluksille", - "ApplyTagsHelpTextHowToApplyIndexers": "Tunnisteiden käyttö valituissa tietolähteissä", + "ApplyTagsHelpTextHowToApplyApplications": "Tunnisteiden käyttö valituille sovelluksille:", + "ApplyTagsHelpTextHowToApplyIndexers": "Tunnisteiden käyttö valituille hakupalveluille:", "ApplyTagsHelpTextRemove": "- \"Poista\" tyhjentää syötetyt tunnisteet", - "ApplyTagsHelpTextReplace": "- \"Korvaa\" nykyiset tunnisteet syötetyillä tai tyhjennä kaikki tunnisteet jättämällä tyhjäksi", - "DownloadClientPriorityHelpText": "Lautaustyökalujen painotus, 1– 50 (korkein-alin). Oletusarvo on 1 ja tasaveroiset erotetaan Round-Robin-tekniikalla.", + "ApplyTagsHelpTextReplace": "– \"Korvaa\" nykyiset tunnisteet syötetyillä tai tyhjennä kaikki tunnisteet jättämällä tyhjäksi.", + "DownloadClientPriorityHelpText": "Useiden latauspalveluiden painotus, 1–50 (korkein-alin). Oletusarvo on 1 ja tasaveroiset erotetaan Round-Robin-tekniikalla.", "Album": "Albumi", "Artist": "Esittäjä", "Author": "Kirjailija", "Book": "Kirja", - "UpdateAvailableHealthCheckMessage": "Uusi päivitys on saatavilla", + "UpdateAvailableHealthCheckMessage": "Uusi päivitys on saatavilla: {version}", "Episode": "Jakso", "Label": "Nimi", "Theme": "Teema", @@ -490,44 +490,44 @@ "WhatsNew": "Mikä on uutta?", "ConnectionLostToBackend": "{appName} kadotti yhteyden taustajärjestelmään ja se on käynnistettävä uudelleen.", "minutes": "minuuttia", - "AddConnection": "Lisää yhteys", - "NotificationStatusAllClientHealthCheckMessage": "Mikään ilmoituspavelu ei ole ongelmien vuoksi käytettävissä.", - "NotificationStatusSingleClientHealthCheckMessage": "Ilmoitukset eivät ole ongelmien vuoksi käytettävissä: {notificationNames}", - "AuthBasic": "Perus (ponnahdusikkuna)", + "AddConnection": "Lisää ilmoituspavelu", + "NotificationStatusAllClientHealthCheckMessage": "Ilmoituspalvelut eivät ole ongelmien vuoksi käytettävissä.", + "NotificationStatusSingleClientHealthCheckMessage": "Ilmoituspalvelut eivät ole ongelmien vuoksi käytettävissä: {notificationNames}.", + "AuthBasic": "Perus (selaimen ponnahdus)", "AuthForm": "Lomake (kirjautumissivu)", - "DisabledForLocalAddresses": "Ei käytössä paikallisille osoitteille", + "DisabledForLocalAddresses": "Ei käytössä paikallisissa osoitteissa", "None": "Ei mitään", "ResetAPIKeyMessageText": "Haluatko varmasti korvata rajapinnan avaimen uudella?", - "TotalIndexerSuccessfulGrabs": "Onnistuneiden tietolähdekaappausten kokonaismäärä", + "TotalIndexerSuccessfulGrabs": "Onnistuneiden hakupalvelukaappausten kokonaismäärä", "AppUpdated": "{appName} on päivitetty", "AppUpdatedVersion": "{appName} on päivitetty versioon {version} ja muutosten käyttöönottamiseksi se on käynnistettävä uudelleen.", - "IndexerDownloadClientHelpText": "Määritä tämän tietolähteen kanssa käytettävä lataustyökalu.", - "AuthenticationRequiredWarning": "Etäkäytön estämiseksi ilman tunnistautumista {appName} vaatii nyt todennuksen käyttöönoton. Todennus voidaan poistaa käytöstä paikallisille osoitteille.", + "IndexerDownloadClientHelpText": "Määritä {appName}in käyttöliittymässä tästä hakupalvelusta kaapattaessa käytettävä latauspalvelu.", + "AuthenticationRequiredWarning": "Etäkäytön estämiseksi ilman tunnistautumista {appName} vaatii nyt tunnistautumisen käyttöönoton. Paikallisilta osoitteilta se voidaan valinnaisesti poistaa käytöstä.", "TotalGrabs": "Kaappausten kokonaismäärä", - "AddDownloadClientImplementation": "Lisäätään lataustyökalua - {implementationName}", - "AddIndexerImplementation": "Lisätään tietolähdettä - {implementationName}", + "AddDownloadClientImplementation": "Lisätään latauspalvelua – {implementationName}", + "AddIndexerImplementation": "Lisätään hakupalvelua – {implementationName}", "OnGrabHelpText": "Kun julkaisu kaapataan", - "ManageDownloadClients": "Hallitse lataustyökaluja", - "NoDownloadClientsFound": "Lataustyökaluja ei löytynyt", - "CountDownloadClientsSelected": "{count} lataustyökalu(a) on valittu", - "EditSelectedDownloadClients": "Muokkaa valittuja lataustyökaluja", - "IndexerDownloadClientHealthCheckMessage": "Tietolähteet virheellisillä lataustyökaluilla: {indexerNames}.", - "AddIndexerProxyImplementation": "Lisää tiedonhaun välityspalvelin - {implementationName}", - "EditIndexerProxyImplementation": "Muokkaa tiedonhaun välityspalvelinta - {implementationName}", - "EditDownloadClientImplementation": "Muokataan lataustyökalua - {implementationName}", + "ManageDownloadClients": "Hallitse palveluita", + "NoDownloadClientsFound": "Latauspalveluita ei löytynyt", + "CountDownloadClientsSelected": "{count} latauspalvelu(a) on valittu", + "EditSelectedDownloadClients": "Muokkaa valittuja latauspalveluita", + "IndexerDownloadClientHealthCheckMessage": "Hakupalvelut virheellisillä latauspalveluilla: {indexerNames}.", + "AddIndexerProxyImplementation": "Lisätään tiedonhaun välityspalvelinta – {implementationName}", + "EditIndexerProxyImplementation": "Muokataan tiedonhaun välityspalvelinta – {implementationName}", + "EditDownloadClientImplementation": "Muokataan latauspalvelua – {implementationName}", "AddCustomFilter": "Lisää oma suodatin", "ApplyChanges": "Toteuta muutokset", "EditSelectedIndexers": "Muokkaa valittuja sisältölähteitä", "NoHistoryFound": "Historiaa ei löytynyt", - "NoIndexersFound": "Tietolähteitä ei löytynyt", + "NoIndexersFound": "Palveluita ei löytynyt", "StopSelecting": "Lopeta valitseminen", - "EditConnectionImplementation": "Muokataan kytköstä - {implementationName}", - "AddConnectionImplementation": "Lisätään kytköstä - {implementationName}", + "EditConnectionImplementation": "Muokataan ilmoituspalvelua – {implementationName}", + "AddConnectionImplementation": "Lisätään ilmoituspavelua – {implementationName}", "DownloadClientQbittorrentSettingsContentLayout": "Sisällön rakenne", - "EditIndexerImplementation": "Muokataan tietolähdettä - {implementationName}", + "EditIndexerImplementation": "Muokataan hakupalvelua – {implementationName}", "AuthenticationRequiredUsernameHelpTextWarning": "Syötä uusi käyttäjätunnus", - "DefaultNameCopiedProfile": "{name} - Kopioi", - "AppsMinimumSeedersHelpText": "Sovellusten edellyttämä tietolähteestä kaapattavien kohteiden jakajien (seed) vähimmäismäärä. Jos tyhjä, käytetään synkronointiprofiilin oletusta.", + "DefaultNameCopiedProfile": "{name} (kopio)", + "AppsMinimumSeedersHelpText": "Sovellusten edellyttämä hakupalvelusta kaapattavien kohteiden jakajien (seed) vähimmäismäärä. Jos tyhjä, käytetään synkronointiprofiilin oletusta.", "TotalHostGrabs": "Isännän kaappausten kokonaismäärä", "IncludeManualGrabsHelpText": "Sisällytä {appName}in käyttöliittymästä tehdyt manuaalikaappaukset.", "AuthenticationRequiredHelpText": "Valitse mitkä pyynnöt vaativat tunnistautumisen. Älä muuta, jos et ymmärrä riskejä.", @@ -537,47 +537,47 @@ "AuthenticationMethod": "Tunnistautumistapa", "Clone": "Monista", "AuthenticationRequiredPasswordConfirmationHelpTextWarning": "Vahvista uusi salasana", - "EditApplicationImplementation": "Muokataan sovellusta - {implementationName}", - "AddApplicationImplementation": "Lisätään sovellusta - {implementationName}", + "EditApplicationImplementation": "Muokataan sovellusta – {implementationName}", + "AddApplicationImplementation": "Lisätään sovellusta – {implementationName}", "InvalidUILanguage": "Käytöliittymän kielivalinta on virheellinen. Korjaa se ja tallenna asetukset.", "SeedRatio": "Jakosuhde", "SeedTime": "Jakoaika", "days": "päivää", "HistoryDetails": "Historiatiedot", - "IndexerDisabled": "Tietolähde ei ole käytössä", + "IndexerDisabled": "Hakupalvelu ei ole käytössä", "AdvancedSettingsShownClickToHide": "Lisäasetukset näytetään, piilota painamalla tästä.", "AdvancedSettingsHiddenClickToShow": "Lisäasetukset on piilotettu, näytä painamalla tästä.", - "AppsMinimumSeeders": "Sovellusten vähimmäisjakajat", + "AppsMinimumSeeders": "Jakajien vähimmäismäärä", "BasicSearch": "Perushaku", "CountApplicationsSelected": "{count} sovellus(ta) on valittu", "DeleteSelectedApplications": "Poista valitut sovellukset", - "DeleteSelectedIndexer": "Poista valittu tietolähde", - "DeleteSelectedIndexers": "Poista valitut tietolähteet", + "DeleteSelectedIndexer": "Poista valittu hakupalvelu", + "DeleteSelectedIndexers": "Poista valitut hakupalvelut", "Implementation": "Toteutus", - "IndexerCategories": "Tietolähdekategoriat", - "IndexerStatus": "Tietolähteen tila", + "IndexerCategories": "Hakupalvelukategoriat", + "IndexerStatus": "Hakupalvelun tila", "ManageApplications": "Hallitse sovelluksia", "NewznabUrl": "Newznab URL", - "PackSeedTime": "Koosteen jakoaika", + "PackSeedTime": "Paketin jakoaika", "PackSeedTimeHelpText": "Aika, joka koostepaketin (kuten sarjan tuotantokauden tai esittäjän diskografian) sisältävää torrentia tulee jakaa. Käytä sovelluksen oletusta jättämällä tyhjäksi.", "QueryType": "Kyselyn tyyppi", - "SearchAllIndexers": "Etsi kaikista tietolähteistä", + "SearchAllIndexers": "Etsi kaikista hakupalveluista", "SeedRatioHelpText": "Jakosuhde, joka torrentin tulee saavuttaa ennen sen pysäytystä. Käytä sovelluksen oletusta jättämällä tyhjäksi.", "TorznabUrl": "Torznab URL", "ApiKeyValidationHealthCheckMessage": "Muuta rajapinnan (API) avain ainakin {length} merkin pituiseksi. Voit tehdä tämän asetuksista tai muokkaamalla asetustiedostoa.", "OnHealthRestored": "Terveystilan vakautuessa", "OnHealthRestoredHelpText": "Terveystilan vakautuessa", "TotalHostQueries": "Isännän kyselyiden kokonaismäärä", - "TotalIndexerQueries": "Tietolähteen kyselyiden kokonaismäärä", + "TotalIndexerQueries": "Hakupalvelun kyselyiden kokonaismäärä", "GoToApplication": "Siirry sovellukseen", - "AreYouSureYouWantToDeleteIndexer": "Haluatko varmasti poistaa tietolähteen \"{name}\" sovelluksesta {appName}?", + "AreYouSureYouWantToDeleteIndexer": "Haluatko varmasti poistaa hakupalvelun \"{name}\" sovelluksesta {appName}?", "AuthQueries": "Todennuskyselyt", - "ApplicationTagsHelpText": "Sovellukseen synkronoidaan yhdellä tai useammalla vastaavalla tunnisteella merkityt tietolähteet. Jos tässä ei ole tunnisteita, ei tietolähteitä synkronoida niiden tunnisteiden vuoksi.", - "ApplicationTagsHelpTextWarning": "Tunnisteita tulee käyttää harkiten, koska niillä voi olla odottamattomia vaikutuksia. Tunnisteella merkittyyn sovellukseen synkronoidaan vain samalla tunnisteella merkityt tietolähteet.", - "AverageResponseTimesMs": "Tietolähteiden keskimääräiset vasteajat (ms)", - "CountIndexersAvailable": "{count} tietolähde(ttä) on käytettävissä", - "FoundCountReleases": "Löytyi {itemCount} julkaisua", - "IndexerTagsHelpTextWarning": "Tunnisteita tulee käyttää harkiten, koska niillä voi olla odottamattomia vaikutuksia. Tunnisteella merkitty tietolähde synkronoidaan vain samalla tunnisteella merkittyyn sovellukseen.", + "ApplicationTagsHelpText": "Sovellukseen synkronoidaan yhdellä tai useammalla vastaavalla tunnisteella merkityt hakupalvelut. Jos tässä ei ole tunnisteita, ei palveluiden synkronointia estetä tunnisteiden perusteella.", + "ApplicationTagsHelpTextWarning": "Tunnisteita tulee käyttää harkiten, koska niillä voi olla odottamattomia vaikutuksia. Tunnisteella merkittyyn sovellukseen synkronoidaan vain samalla tunnisteella merkityt hakupalvelut.", + "AverageResponseTimesMs": "hakupalveluiden keskimääräiset vasteajat (ms)", + "CountIndexersAvailable": "{count} hakupalvelu(a) on käytettävissä", + "FoundCountReleases": "Löydettiin {itemCount} julkaisua", + "IndexerTagsHelpTextWarning": "Tunnisteita tulee käyttää harkiten, koska niillä voi olla odottamattomia vaikutuksia. Tunnisteella merkitty hakupalvelu synkronoidaan vain samalla tunnisteella merkittyihin sovelluksiin.", "LastFailure": "Edellinen virhe", "SelectedCountOfCountReleases": "Valittu {selectedCount}/{itemCount} julkaisua", "TotalQueries": "Kyselyiden kokonaismäärä", @@ -587,99 +587,224 @@ "AddApplication": "Lisää sovellus", "AddCategory": "Lisää kategoria", "EditCategory": "Muokkaa kategoriaa", - "IndexerId": "Tietolähteen ID", - "ActiveIndexers": "Aktiiviset tietolähteet", - "NoIndexerCategories": "Tietolähteelle ei löytynyt kategorioita", + "IndexerId": "Hakupalvelun ID", + "ActiveIndexers": "Aktiiviset hakupalvelut", + "NoIndexerCategories": "Hakupalvelulle ei löytynyt kategorioita", "ActiveApps": "Aktiiviset sovellukset", - "NoIndexerHistory": "Tietolähteelle ei löytynyt historiaa", + "NoIndexerHistory": "Hakupalvelulle ei löytynyt historiaa", "DownloadClientQbittorrentSettingsContentLayoutHelpText": "Määrittää käytetäänkö qBittorrentista määritettyä rakennetta, torrentin alkuperäistä rakennetta vai luodaanko uusi alikansio (qBittorrent 4.3.2+).", "External": "Ulkoinen", - "IndexerFailureRate": "Tietolähteen virhetaajuus", - "IndexerHistoryLoadError": "Virhe ladattaessa tietolähteen historiaa", + "IndexerFailureRate": "Hakupalvelun virhetaajuus", + "IndexerHistoryLoadError": "Virhe ladattaessa hakupalvelun historiaa", "InitialFailure": "Alkuperäinen virhe", "PasswordConfirmation": "Salasanan vahvistus", "RepeatSearch": "Toista haku", - "SearchCountIndexers": "Etsi {count} tietolähteestä", + "SearchCountIndexers": "Etsi {count} hakupalvelusta", "SearchQueries": "Hakukyselyt", "SeedTimeHelpText": "Aika, joka torrentia tulee jakaa ennen sen pysäytystä. Käytä sovelluksen oletusta jättämällä tyhjäksi.", "RssQueries": "RSS-kyselyt", "TotalUserAgentQueries": "Käyttäjäagentin kyselyiden kokonaismäärä", "NotificationsEmailSettingsUseEncryption": "Käytä salausta", "NotificationsEmailSettingsUseEncryptionHelpText": "Määrittää suositaanko salausta, jos se on määritetty palvelimelle, käytetäänkö aina SSL- (vain portti 465) tai StartTLS-salausta (kaikki muut portit), voi käytetäänkö salausta lainkaan.", - "ManageClients": "Hallitse työkaluja", + "ManageClients": "Hallitse palveluita", "NoApplicationsFound": "Sovelluksia ei löytynyt", - "DownloadClientAriaSettingsDirectoryHelpText": "Valinnainen latuasten tallennussijainti. Käytä Aria2-oletusta jättämällä tyhjäksi.", - "UrlBaseHelpText": "Lisää {appName}in URL-osoitteeseen jälkiliitteen, esim. \"http://[osoite]:[portti]/[URL-perusta]\". Oletusarvo on tyhjä.", + "DownloadClientAriaSettingsDirectoryHelpText": "Vaihtoehtoinen latausten tallennussijainti. Käytä Aria2:n oletusta jättämällä tyhjäksi.", + "UrlBaseHelpText": "Käänteisen välityspalvelimen tukea varten. Oletusarvo on tyhjä.", "Donate": "Lahjoita", "DownloadClientFloodSettingsAdditionalTagsHelpText": "Lisää median ominaisuuksia tunnisteina. Vihjeet ovat esimerkkejä.", - "DownloadClientRTorrentSettingsDirectoryHelpText": "Valinnainen latuasten tallennussijainti. Käytä Aria2-oletusta jättämällä tyhjäksi.", - "DownloadClientSettingsUseSslHelpText": "Muodosta {clientName} -yhteys käyttäen salattua yhteyttä.", - "DownloadClientTransmissionSettingsDirectoryHelpText": "Vaihtoehtoinen latauskansio. Käytä Transmissionin oletusta jättämällä tyhjäksi.", - "DownloadClientTransmissionSettingsUrlBaseHelpText": "Lisää etuliite lataustyökalun {clientName} RPC-URL-osoitteeseen. Esimerkiksi {url}. Oletus on \"{defaultUrl}\".", - "IndexerSettingsAppsMinimumSeedersHelpText": "Sovellusten edellyttämä tietolähteestä kaapattavien kohteiden jakajien (seed) vähimmäismäärä. Jos tyhjä, käytetään synkronointiprofiilin oletusta.", + "DownloadClientRTorrentSettingsDirectoryHelpText": "Vaihtoehtoinen latausten tallennussijainti. Käytä rTorrentin oletusta jättämällä tyhjäksi.", + "DownloadClientSettingsUseSslHelpText": "Muodosta {clientName}-yhteys käyttäen salattua yhteyttä.", + "DownloadClientTransmissionSettingsDirectoryHelpText": "Vaihtoehtoinen latausten tallennussijainti. Käytä Transmissionin oletusta jättämällä tyhjäksi.", + "DownloadClientTransmissionSettingsUrlBaseHelpText": "Lisää latauspalvelun {clientName} RPC-URL-osoitteeseen etuliitteen, esim. \"{url}\". Oletus on \"{defaultUrl}\".", + "IndexerSettingsAppsMinimumSeedersHelpText": "Sovellusten edellyttämä hakupalvelusta kaapattavien kohteiden jakajien (seed) vähimmäismäärä. Jos tyhjä, käytetään synkronointiprofiilin oletusta.", "Menu": "Valikko", - "ApplicationSettingsSyncRejectBlocklistedTorrentHashes": "Hylkää estetyt torrent-hajautusarvot kaapattaessa", - "IndexerBeyondHDSettingsSearchTypes": "Mitä etsitään", + "ApplicationSettingsSyncRejectBlocklistedTorrentHashes": "Synkronoi estetyt torrent-hajautusarvot kaapattaessa", + "IndexerBeyondHDSettingsSearchTypes": "Etsittävät tyypit", "IndexerSettingsSeedRatio": "Jakosuhde", "IndexerSettingsSeedTime": "Jakoaika", - "IndexerSettingsSeedTimeHelpText": "Aika, joka torrentia tulee jakaa ennen sen pysäytystä. Käytä lataustyökalun oletusta jättämällä tyhjäksi.", + "IndexerSettingsSeedTimeHelpText": "Aika, joka torrentia tulee jakaa ennen sen pysäytystä. Käytä latauspalvelun oletusta jättämällä tyhjäksi.", "IndexerSettingsVipExpiration": "VIP-erääntyy", "Destination": "Kohde", "Directory": "Kansio", - "DownloadClientFloodSettingsTagsHelpText": "Latauksen alkuperäiset tunnisteet. Jotta se voidaa tunnistaa, on latauksella oltava sen alkuperäiset tunnisteet. Tämä välttää ristiriidat muiden latausten kanssa.", - "DownloadClientFreeboxSettingsApiUrl": "Rajapinnan URL-osoite", + "DownloadClientFloodSettingsTagsHelpText": "Latauksen alkuperäiset tunnisteet, jotka tarvitaan sen tunnistamiseen. Tämä välttää ristiriidat muiden latausten kanssa.", + "DownloadClientFreeboxSettingsApiUrl": "Rajapinnan URL", "DownloadClientFreeboxSettingsAppTokenHelpText": "Freebox-rajapinnan käyttöoikeutta määritettäessä saatu app_token-tietue.", "DownloadClientFreeboxSettingsHostHelpText": "Freeboxin isäntänimi tai IP-osoite. Oletus on \"{url}\" (toimii vain samassa verkossa).", "DownloadClientPneumaticSettingsStrmFolder": "Strm-kansio", "DownloadClientQbittorrentSettingsInitialStateHelpText": "Tila, jossa torrentit lisätään qBittorrentiin. Huomioi, että pakotetut torrentit eivät noudata nopeusrajoituksia.", - "DownloadClientSettingsAddPaused": "Lisää pysäytettynä", + "DownloadClientSettingsAddPaused": "Lisää keskeytettynä", "DownloadClientSettingsDestinationHelpText": "Määrittää manuaalisen tallennuskohteen. Käytä oletusta jättämällä tyhjäksi.", - "DownloadClientSettingsInitialState": "Virheellinen tila", - "DownloadClientSettingsInitialStateHelpText": "Lataustyökaluun {clientName} lisättyjen torrentien aloitustila.", + "DownloadClientSettingsInitialState": "Aloitustila", + "DownloadClientSettingsInitialStateHelpText": "Latauspalveluun {clientName} lisättyjen torrentien aloitustila.", "IndexerHDBitsSettingsCodecs": "Koodekit", "IndexerHDBitsSettingsCodecsHelpText": "Jos ei määritetty, käytetään kaikkia vaihtoehtoja.", - "IndexerHDBitsSettingsMediums": "Mediatyypit", + "IndexerHDBitsSettingsMediums": "Muodot", "IndexerHDBitsSettingsMediumsHelpText": "Jos ei määritetty, käytetään kaikkia vaihtoehtoja.", "IndexerHDBitsSettingsOriginsHelpText": "Jos ei määritetty, käytetään kaikkia vaihtoehtoja.", "IndexerSettingsAdditionalParameters": "Muut parametrit", - "IndexerSettingsApiPath": "API:n polku", - "IndexerSettingsApiPathHelpText": "Polku API:in (yleensä {url}).", + "IndexerSettingsApiPath": "Rajapinnan sijainti", + "IndexerSettingsApiPathHelpText": "Rajapinnan sijainti, yleensä \"{url}\".", "IndexerSettingsCookie": "Eväste", - "IndexerSettingsPackSeedTime": "Koosteen jakoaika", + "IndexerSettingsPackSeedTime": "Paketin jakoaika", "IndexerSettingsPackSeedTimeIndexerHelpText": "Aika, joka koostepaketin (kuten sarjan tuotantokauden tai esittäjän diskografian) sisältävää torrentia tulee jakaa. Käytä sovelluksen oletusta jättämällä tyhjäksi.", - "IndexerSettingsSeedRatioHelpText": "Suhde, joka torrentin tulee saavuttaa ennen sen pysäytystä. Käytä lataustyökalun oletusta jättämällä tyhjäksi. Suhteen tulisi olla ainakin 1.0 ja noudattaa tietolähteen sääntöjä.", + "IndexerSettingsSeedRatioHelpText": "Suhde, joka torrentin tulee saavuttaa ennen sen pysäytystä. Käytä latauspalvelun oletusta jättämällä tyhjäksi. Suhteen tulisi olla ainakin 1.0 ja noudattaa hakupalvelun sääntöjä.", "SecretToken": "Salainen tunniste", "TorrentBlackholeSaveMagnetFiles": "Tallenna magnet-tiedostot", "UseSsl": "Käytä SSL-salausta", - "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "Jos torrent on estetty hajautusarvon perusteella sitä ei välttämättä hylätä oikein etsittäessä joiltakin tietolähteiltä RSS-syötteen tai haun välityksellä. Tämä mahdollistaa tällaisten torrentien hylkäämisen kaappauksen jälkeen, mutta ennen välitystä lataustyökalulle.", + "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "Jos torrent on estetty hajautusarvon perusteella sitä ei välttämättä hylätä oikein joidenkin hakupalveluiden RSS-syötteestä tai hausta. Tämän käyttöönotto mahdollistaa tällaisten torrentien hylkäämisen kaappauksen jälkeen, kuitenkin ennen kuin niitä välitetään latauspalvelulle.", "BlackholeFolderHelpText": "Kansio, jonne {appName} tallentaa {extension}-tiedoston.", - "DownloadClientDelugeSettingsUrlBaseHelpText": "Lisää etuliitteen Delugen JSON-URL-osoitteeseen (ks. {url}).", + "DownloadClientDelugeSettingsUrlBaseHelpText": "Lisää Delugen JSON-URL-osoitteeseen etuliitteen, ks. \"{url})\".", "DownloadClientFloodSettingsAdditionalTags": "Lisätunnisteet", "DownloadClientPneumaticSettingsStrmFolderHelpText": "Tämän kansion .strm-tiedostot tuodaan droonilla.", "DownloadClientQbittorrentSettingsSequentialOrderHelpText": "Lataa tiedostot järjestyksessä (qBittorrent 4.1.0+).", "UsenetBlackholeNzbFolder": "NZB-kansio", "XmlRpcPath": "XML RPC -sijainti", - "DownloadClientSettingsUrlBaseHelpText": "Lisää etuliite lataustuökalun {clientName} URL-osoitteeseen, kuten {url}.", - "DownloadClientFloodSettingsUrlBaseHelpText": "Lisää etuliitteen Flood-rajapintaan (esim. {url}).", - "DownloadClientDownloadStationSettingsDirectoryHelpText": "Valinnainen jaettu kansio latauksille. Download Stationin oletussijaintia jättämällä tyhjäksi.", - "DownloadClientFreeboxSettingsApiUrlHelpText": "Määritä Freebox-rajapinnan perus-URL rajapinnan versiolla. Esimerkiksi \"{url}\". Oletus on \"{defaultApiUrl}\".", + "DownloadClientSettingsUrlBaseHelpText": "Lisää lataustuökalun {clientName} URL-osoitteeseen etuliitteen, esim. \"{url}\".", + "DownloadClientFloodSettingsUrlBaseHelpText": "Lisää Flood-rajapintaan etuliitteen, esim. \"{url}\".", + "DownloadClientDownloadStationSettingsDirectoryHelpText": "Vaihtoehtoinen jaettu kansio latauksille. Käytä Download Stationin oletussijaintia jättämällä tyhjäksi.", + "DownloadClientFreeboxSettingsApiUrlHelpText": "Määritä Freebox-rajapinnan perus-URL rajapinnan versiolla, esim. \"{url}\". Oletus on \"{defaultApiUrl}\".", "DownloadClientQbittorrentSettingsFirstAndLastFirst": "Ensimmäinen ja viimeinen ensin", - "DownloadClientFreeboxSettingsAppId": "Sovellustunniste", - "DownloadClientFreeboxSettingsPortHelpText": "Freebox-liittymän portti. Oletus on \"{port}\".", + "DownloadClientFreeboxSettingsAppId": "Sovelluksen ID", + "DownloadClientFreeboxSettingsPortHelpText": "Freebox-liittymän portti. Oletus on {port}.", "DownloadClientPneumaticSettingsNzbFolder": "NZB-kansio", "DownloadClientQbittorrentSettingsSequentialOrder": "Peräkkäinen järjestys", "CustomFilter": "Oma suodatin", "DownloadClientFreeboxSettingsAppIdHelpText": "Freebox-rajapinnan käyttöoikeutta määritettäessä käytettävä App ID -sovellustunniste.", "DownloadClientFreeboxSettingsAppToken": "Sovellustietue", - "DownloadClientNzbgetSettingsAddPausedHelpText": "Tämä vaatii vähintään NzbGet-version 16.0.", - "DownloadClientPneumaticSettingsNzbFolderHelpText": "Tämän kansion on oltava tavoitettavissa XBMC:stä.", - "DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText": "Aloita lataamalla ensimmäinen ja viimeinen osa (qBittorrent 4.1.0+).", + "DownloadClientNzbgetSettingsAddPausedHelpText": "Tämä vaatii vähintään NzbGetin version 16.0.", + "DownloadClientPneumaticSettingsNzbFolderHelpText": "Tämän kansion on oltava Kodin tavoitettavissa.", + "DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText": "Lataa ensimmäinen ja viimeinen osa ensin (qBittorrent 4.1.0+).", "DownloadClientQbittorrentSettingsUseSslHelpText": "Käytä suojattua yhteyttä. Katso qBittorentin asetusten \"Selainkäyttö\"-osion \"Käytä HTTPS:ää HTTP:n sijaan\" -asetus.", "DownloadClientRTorrentSettingsAddStopped": "Lisää pysäytettynä", "DownloadClientRTorrentSettingsUrlPath": "URL-sijainti", - "TorrentBlackholeSaveMagnetFilesHelpText": "Tallenna magnet-linkki, jos .torrent-tiedostoa ei ole käytettävissä (hyödyllinen vain lataustyökalun tukiessa tiedostoon tallennettuja magnet-linkkejä).", + "TorrentBlackholeSaveMagnetFilesHelpText": "Tallenna magnet-linkki, jos .torrent-tiedostoa ei ole käytettävissä (hyödyllinen vain latauspalvelun tukiessa tiedostoon tallennettuja magnet-linkkejä).", "TorrentBlackholeTorrentFolder": "Torrent-kansio", - "TorrentBlackholeSaveMagnetFilesExtension": "Tallennettujen magnet-tiedostojen pääte", + "TorrentBlackholeSaveMagnetFilesExtension": "Tallenna magnet-tiedostojen pääte", "TorrentBlackholeSaveMagnetFilesExtensionHelpText": "Magnet-linkeille käytettävä tiedostopääte. Oletus on \".magnet\".", - "LabelIsRequired": "Nimi on pakollinen" + "LabelIsRequired": "Nimi on pakollinen", + "Default": "Oletus", + "GrabRelease": "Kaappaa julkaisu", + "OverrideGrabModalTitle": "Ohitetaan ja kaapataan – {title}", + "PrioritySettings": "Painotus: {priority}", + "SelectDownloadClientModalTitle": "{modalTitle} – Valitse latauspalvelu", + "ProxyValidationBadRequest": "Välityspalvelintesti epäonnistui. Tilakoodi: {statusCode}.", + "ProxyValidationUnableToConnect": "Välityspalvelinta ei tavoiteta: {exceptionMessage}. Saat lisätietoja virheen lähellä olevista lokimerkinnöistä.", + "ManualGrab": "Manuaalikaappaus", + "OverrideAndAddToDownloadClient": "Ohita ja lisää etälatauspalveluun", + "BuiltIn": "Sisäänrakennettu", + "Any": "Mikä tahansa", + "Script": "Komentosarja", + "InfoUrl": "Tietojen URL", + "PublishedDate": "Julkaisupäivä", + "Redirected": "Uudelleenohjattu", + "AllSearchResultsHiddenByFilter": "Aktiivinen suodatin piilottaa kaikki tulokset.", + "HealthMessagesInfoBox": "Saat lisätietoja näiden vakausviestien syistä painamalla rivin lopussa olevaa wikilinkkiä (kirjakuvake) tai tarkastelemalla [lokitietoja]({link}). Mikäli et osaa tulkita näitä viestejä, tavoitat tukemme alla olevilla linkeillä.", + "PackageVersionInfo": "{packageVersion} julkaisijalta {packageAuthor}", + "ErrorRestoringBackup": "Virhe palautettaessa varmuuskopiota", + "ExternalUpdater": "{appName} on määritetty käyttämään ulkoista päivitysratkaisua.", + "FailedToFetchUpdates": "Päivitysten nouto epäonnistui", + "AptUpdater": "Asenna päivitys APT-työkalun avulla", + "DockerUpdater": "Hanki päivitys päivittämällä Docker-säiliö", + "Download": "Lataa", + "LogFilesLocation": "Lokitiedostojen tallennussijainti: {location}", + "Logout": "Kirjaudu ulos", + "NoEventsFound": "Tapahtumia ei löytynyt", + "RestartReloadNote": "Huomioi: {appName} käynnistyy palautusprosessin aikana automaattisesti uudelleen.", + "TheLogLevelDefault": "Lokikirjauksen oletusarvoinen laajuus on \"Informatiivinen\". Laajuutta voidaan muuttaa [Yleisistä asetuksista](/settings/general).", + "UpdateAppDirectlyLoadError": "{appName}ia ei voida päivittää suoraan,", + "UpdaterLogFiles": "Päivittäjän lokitiedostot", + "WouldYouLikeToRestoreBackup": "Haluatko palauttaa varmuuskopion \"{name}\"?", + "InstallLatest": "Asenna uusin", + "CurrentlyInstalled": "Käytössä oleva versio", + "PreviouslyInstalled": "Aiemmin käytössä ollut versio", + "Mixed": "Sekoitettu", + "IndexerSettingsAppsMinimumSeeders": "Jakajien vähimmäismäärä", + "FailedToFetchSettings": "Asetusten nouto epäonnistui", + "IndexerAlphaRatioSettingsExcludeSceneHelpText": "Älä huomioi tulosten SCENE-julkaisuja.", + "DownloadClientRTorrentSettingsUrlPathHelpText": "Polku XMLRPC-päätteeseen, ks. \"{url}\". Käytettäessä ruTorrentia tämä on yleensä RPC2 tai [ruTorrentin sijainti]{url2}.", + "InstallMajorVersionUpdateMessageLink": "Saat lisätietoja osoitteesta [{domain}]({url}).", + "Install": "Asenna", + "NotificationsTelegramSettingsIncludeAppName": "Sisällytä {appName} otsikkoon", + "InstallMajorVersionUpdate": "Asenna päivitys", + "InstallMajorVersionUpdateMessage": "Tämä päivitys asentaa uuden pääversion, joka ei välttämättä ole yhteensopiva laitteistosi kanssa. Haluatko varmasti asentaa päivityksen?", + "NotificationsTelegramSettingsIncludeAppNameHelpText": "Ilmoitukset voidaan tarvittaessa erottaa muista sovelluksista lisäämällä niiden eteen \"{appName}\".", + "DownloadClientRTorrentSettingsAddStoppedHelpText": "Tämä lisää torrentit ja magnet-linkit rTorentiin pysäytetyssä tilassa. Tämä saattaa rikkoa margnet-tiedostot.", + "DownloadClientSettingsPriorityItemHelpText": "Kaapatuille kohteille käytettävä painotus.", + "LogSizeLimit": "Lokin kokorajoitus", + "LogSizeLimitHelpText": "Lokitiedoston enimmäiskoko ennen pakkausta. Oletusarvo on 1 Mt.", + "IndexerAlphaRatioSettingsExcludeScene": "Ohita SCENE-julkaisut", + "AverageGrabs": "Kaappausten keskiarvo", + "AverageQueries": "Kyselyiden keskiarvo", + "IndexerNewznabSettingsAdditionalParametersHelpText": "Muut Newznab-parametrit", + "IndexerNewznabSettingsVipExpirationHelpText": "Syötä VIP-tilan päättymispäivä (yyyy-mm-dd) tai jätä tyhjäksi. {appName} ilmoittaa viikko ennen VIP-tilan päättymistä.", + "IndexerSettingsQueryLimitHelpText": "Kyselyiden enimmäismäärä, jonka {appName} sallii hakupalvelulle.", + "IndexerHDBitsSettingsPasskeyHelpText": "Pääsyavain käyttäjätiedoista", + "DefaultCategory": "Oletuskategoria", + "ClickToChangeQueryOptions": "Muuta kyselyasetuksia painamalla tästä.", + "IndexerSettingsQueryLimit": "Kyselyrajoitus", + "IndexerAvistazSettingsPidHelpText": "PID \"My Account\" tai \"My Profile\" -sivulta.", + "IndexerAvistazSettingsUsernameHelpTextWarning": "Vain vähintään jäsen-tasoinen käyttäjä voi käyttää tämän hakupalvelun rajapintaa.", + "IndexerBeyondHDSettingsLimitedOnly": "Vain rajoitetut", + "IndexerBeyondHDSettingsFreeleechOnlyHelpText": "Etsi vain Freeleech-julkaisuja.", + "IndexerFileListSettingsFreeleechOnlyHelpText": "Etsi vain freeleech-julkaisuja.", + "IndexerGazelleGamesSettingsApiKeyHelpTextWarning": "Avaimella on oltava \"User\" ja \"Torrents\" oikeudet.", + "IndexerHDBitsSettingsUseFilenames": "Käytä tiedostonimiä", + "IndexerMTeamTpSettingsFreeleechOnlyHelpText": "Etsi vain freeleech-julkaisuja.", + "IndexerIPTorrentsSettingsCookieUserAgent": "Evästeen käyttäjäagentti", + "IndexerIPTorrentsSettingsCookieUserAgentHelpText": "Evästeeseen liitetty selaimen käyttäjäagentti.", + "IndexerPassThePopcornSettingsApiKeyHelpText": "Sivuston rajapinnan avain", + "IndexerOrpheusSettingsApiKeyHelpText": "Sivuston rajapinnan avain. Löytyy kohdasta \"Settings\" > \"Access Settings\".", + "Open": "Avaa", + "PreferMagnetUrl": "Suosi magnet-URL:eja", + "ProwlarrDownloadClientsAlert": "Latauspalvelut on määritettävä tässä vain, jos hakuja aiotaan tehdä suoraan {appName}ista. Sovelluksien hauille käytetään niiden omien asetusten latauspalvelumäärityksiä.", + "IndexerSettingsLimitsUnitHelpText": "Hakupalveluiden aikarajoituksiin käytettävä yksikkö.", + "IndexerGazelleGamesSettingsSearchGroupNames": "Etsi ryhmien nimillä", + "DownloadClientSettingsDefaultCategoryHelpText": "Oletusarvoinen varakategoria julkaisuille, joilla ei ole kategoriaa. {appName}-kategorian lisääminen välttää ristiriidat ei-{appName} latausten kanssa. Kategoria on valinnainen, mutta erittäin suositeltava.", + "IndexerAvistazSettingsFreeleechOnlyHelpText": "Etsi vain freeleech-julkaisuja.", + "PreferMagnetUrlHelpText": "Hakupalvelu käyttää kaappaukseen ensisijaisesti magnet-linkkejä ja varmistuksena torrent-linkkejä.", + "ProwlarrDownloadClientsInAppOnlyAlert": "Latauspalvelut ovat vain {appName}in sisäisiä hakuja varten, eikä niitä synkronoida muihin sovelluksiin (ominaisuutta ole myöskään suunnitteilla).", + "DownloadClientSettingsDefaultCategorySubFolderHelpText": "Oletusarvoinen varakategoria julkaisuille, joilla ei ole kategoriaa. {appName}-kategorian lisääminen välttää ristiriidat ei-{appName} latausten kanssa. Kategoria on valinnainen, mutta erittäin suositeltava. Luo kohdekansioon [kategoria]-alikansion.", + "IndexerAlphaRatioSettingsFreeleechOnlyHelpText": "Etsi vain freeleech-julkaisuja.", + "IndexerAvistazSettingsUsernameHelpText": "Sivuston käyttäjätunnus", + "IndexerBeyondHDSettingsRefundOnly": "Vain \"Refund\"", + "IndexerBeyondHDSettingsRefundOnlyHelpText": "Etsi vain Refund-julkaisuja.", + "IndexerBeyondHDSettingsRewindOnly": "Vain \"Rewind\"", + "IndexerBeyondHDSettingsRewindOnlyHelpText": "Etsi vain Rewind-julkaisuja.", + "IndexerBeyondHDSettingsApiKeyHelpText": "Sivuston rajapinnan avain. Löytyy kohdasta \"My Security\" > \"API Key\".", + "IndexerBeyondHDSettingsSearchTypesHelpText": "Valitse halutut julkaisutyypit. Jos mitään ei ole valittu käytetään kaikkia.", + "IndexerFileListSettingsPasskeyHelpText": "Sivuston pääsyavain. Tämä on latauspalvelussasi näkyvä trakkerin URL-osoitteen aakkosnumeerinen osa.", + "IndexerGazelleGamesSettingsFreeleechOnlyHelpText": "Etsi vain freeleech-julkaisuja.", + "IndexerGazelleGamesSettingsApiKeyHelpText": "Sivuston rajapinnan avain. Löytyy kohdasta \"Settings\" > \"Access Settings\".", + "IndexerFileListSettingsUsernameHelpText": "Sivuston käyttäjätunnus", + "IndexerGazelleGamesSettingsSearchGroupNamesHelpText": "Etsi julkaisuja ryhmien nimillä.", + "IndexerHDBitsSettingsFreeleechOnlyHelpText": "Näytä vain Freeleech-julkaisut.", + "IndexerNzbIndexSettingsApiKeyHelpText": "Sivuston rajapinnan avain", + "IndexerHDBitsSettingsOrigins": "Alkuperä", + "IndexerPassThePopcornSettingsFreeleechOnlyHelpText": "Etsi vain freeleech-julkaisuja.", + "IndexerHDBitsSettingsUseFilenamesHelpText": "Käsittele torrentien tiedostonimiä julkaisujen nimikkeinä.", + "IndexerHDBitsSettingsUsernameHelpText": "Sivuston käyttäjätunnus", + "IndexerSettingsGrabLimitHelpText": "Kaappausten enimmäismäärä, jonka {appName} sallii hakupalvelulle.", + "IndexerRedactedSettingsApiKeyHelpText": "Sivuston rajapinnan avain. Löytyy kohdasta \"Settings\" > \"Access Settings\".", + "IndexerSettingsApiUser": "Rajapinnan käyttäjä", + "IndexerSettingsBaseUrl": "Perus-URL", + "IndexerSettingsBaseUrlHelpText": "Määritä verkkotunnus, jota {appName} käyttää sivustolle lähetettäville pyynnöille.", + "IndexerPassThePopcornSettingsApiUserHelpText": "Nämä löytyvät PassThePopcorn-tilin asetuksista, kohdasta \"Edit Profile\" > \"Security\".", + "IndexerSettingsPasskey": "Pääsyavain", + "IndexerBeyondHDSettingsLimitedOnlyHelpText": "Etsi vain Freeleech-julkaisuja (rajoitettu UL).", + "IndexerBeyondHDSettingsRssKeyHelpText": "Sivuston RSS-avain. Löytyy kohdasta \"My Security\" > \"RSS-avain\".", + "IndexerIPTorrentsSettingsFreeleechOnlyHelpText": "Etsi vain freeleech-julkaisuja.", + "IndexerNewznabSettingsApiKeyHelpText": "Sivuston rajapinnan avain", + "IndexerNebulanceSettingsApiKeyHelpText": "Rajapinnan avain kohdasta \"Settings\" > \"Api Keys\".\nAvaimella on oltava \"List\" ja \"Download\" oikeudet.", + "IndexerSettingsGrabLimit": "Kaappausrajoitus", + "IndexerSettingsLimitsUnit": "Rajoitusten yksikkö", + "IndexerSettingsRssKey": "RSS-avain", + "IndexerMTeamTpSettingsApiKeyHelpText": "Sivuston rajapinnan avain. Löytyy kohdasta \"Control Panel\" > \"Security\" > \"Laboratory\".", + "IndexerSettingsPreferMagnetUrlHelpText": "Hakupalvelu käyttää kaappaukseen ensisijaisesti magnet-linkkejä ja varmistuksena torrent-linkkejä.", + "IndexerTorrentSyndikatSettingsApiKeyHelpText": "Sivuston rajapinnan avain", + "IndexerSettingsPreferMagnetUrl": "Suosi magnet-URL:eja", + "IndexerPassThePopcornSettingsGoldenPopcornOnly": "Vain \"Golden Popcorn\"", + "IndexerPassThePopcornSettingsGoldenPopcornOnlyHelpText": "Etsi vain ns. kultaisella pocornilla merkittyjä julkaisuja.", + "IndexerSettingsFreeleechOnly": "Vain \"Freeleech\"", + "IndexerSettingsCookieHelpText": "Jos sivusto vaatii kirjautumisevästeen, on se noudettava selaimen avulla.", + "IndexerAvistazSettingsPasswordHelpText": "Sivuston salasana" } diff --git a/src/NzbDrone.Core/Localization/Core/fr.json b/src/NzbDrone.Core/Localization/Core/fr.json index 9ea26a23c..8768f9c50 100644 --- a/src/NzbDrone.Core/Localization/Core/fr.json +++ b/src/NzbDrone.Core/Localization/Core/fr.json @@ -12,7 +12,7 @@ "Events": "Événements", "Edit": "Modifier", "DownloadClientStatusAllClientHealthCheckMessage": "Aucun client de téléchargement n'est disponible en raison d'échecs", - "DownloadClients": "Clients de téléchargement", + "DownloadClients": "Clients de télécharg.", "Dates": "Dates", "Date": "Date", "Delete": "Supprimer", @@ -57,7 +57,7 @@ "Proxy": "Proxy", "Protocol": "Protocole", "Options": "Options", - "NoChanges": "Aucuns changements", + "NoChanges": "Aucun changement", "NoChange": "Pas de changement", "MoreInfo": "Plus d'informations", "Grabbed": "Saisie", @@ -113,11 +113,11 @@ "Message": "Message", "Level": "Niveau", "KeyboardShortcuts": "Raccourcis clavier", - "HealthNoIssues": "Aucun problème avec votre configuration", - "SystemTimeCheckMessage": "L'heure du système est décalée de plus d'un jour. Les tâches planifiées peuvent ne pas s'exécuter correctement tant que l'heure ne sera pas corrigée", + "NoIssuesWithYourConfiguration": "Aucun problème avec votre configuration", + "SystemTimeHealthCheckMessage": "L'heure du système est décalée de plus d'un jour. Les tâches planifiées peuvent ne pas s'exécuter correctement tant que l'heure ne sera pas corrigée", "SettingsShowRelativeDates": "Afficher les dates relatives", "UnsavedChanges": "Modifications non enregistrées", - "ShowSearchHelpText": "Afficher le bouton de recherche au survol", + "ShowSearchHelpText": "Affiche le bouton de recherche au survol", "ShowSearch": "Afficher la recherche", "SettingsTimeFormat": "Format de l'heure", "SettingsShowRelativeDatesHelpText": "Afficher les dates relatives (aujourd'hui, hier, etc.) ou absolues", @@ -214,7 +214,7 @@ "UnableToLoadHistory": "Impossible de charger l'historique", "UnableToLoadGeneralSettings": "Impossible de charger les paramètres généraux", "DownloadClientsLoadError": "Impossible de charger les clients de téléchargement", - "UnableToLoadBackups": "Impossible de charger les sauvegardes", + "BackupsLoadError": "Impossible de charger les sauvegardes", "UnableToAddANewNotificationPleaseTryAgain": "Impossible d'ajouter une nouvelle notification, veuillez réessayer.", "UnableToAddANewIndexerPleaseTryAgain": "Impossible d'ajouter un nouvel indexeur, veuillez réessayer.", "UnableToAddANewDownloadClientPleaseTryAgain": "Impossible d'ajouter un nouveau client de téléchargement, veuillez réessayer.", @@ -339,7 +339,7 @@ "Apps": "Applications", "Auth": "Auth", "Category": "Catégorie", - "Custom": "Customisé", + "Custom": "Personnaliser", "DeleteAppProfile": "Supprimer le profil de l'application", "Description": "Description", "Donations": "Dons", @@ -383,8 +383,8 @@ "HistoryCleanupDaysHelpText": "Définir sur 0 pour désactiver le nettoyage automatique", "HistoryCleanupDaysHelpTextWarning": "Les fichiers dans la corbeille plus anciens que le nombre de jours sélectionné seront nettoyés automatiquement", "OnGrab": "Récupéré à la sortie", - "OnHealthIssue": "Sur la question de la santé", - "TestAllIndexers": "Testez tous les indexeurs", + "OnHealthIssue": "Lors de problème de santé", + "TestAllIndexers": "Tester tous les indexeurs", "UserAgentProvidedByTheAppThatCalledTheAPI": "User-Agent fourni par l'application qui a appelé l'API", "Database": "Base de données", "HistoryCleanup": "Nettoyage de l'historique", @@ -404,9 +404,9 @@ "Website": "Site internet", "AudioSearch": "Recherche de musique", "BookSearch": "Recherche de livres", - "OnApplicationUpdate": "Sur la mise à jour de l'application", - "OnApplicationUpdateHelpText": "Lors de la mise à jour de l'app", - "IndexerNoDefCheckMessage": "Les indexeurs ne sont pas définis et ne fonctionneront pas :Merci de les retirer et (ou) les ajouter à nouveau à {appName}", + "OnApplicationUpdate": "Lors de la mise à jour de l'application", + "OnApplicationUpdateHelpText": "Lors de la mise à jour de l'application", + "IndexerNoDefinitionCheckHealthCheckMessage": "Les indexeurs ne sont pas définis et ne fonctionneront pas : {indexerNames}. Merci de les retirer et (ou) les ajouter à nouveau à {appName}.", "MovieSearch": "Recherche de films", "TvSearch": "Recherche de séries TV", "Application": "Applications", @@ -458,7 +458,7 @@ "AuthenticationRequiredWarning": "Pour empêcher l'accès à distance sans authentification, {appName} exige désormais que l'authentification soit activée. Vous pouvez éventuellement désactiver l'authentification pour les adresses locales.", "Remove": "Retirer", "Replace": "Remplacer", - "TheLatestVersionIsAlreadyInstalled": "La dernière version de {appName} est déjà installée", + "OnLatestVersion": "La dernière version de {appName} est déjà installée", "AddCustomFilter": "Ajouter filtre personnalisé", "AddApplication": "Ajouter une application", "IncludeManualGrabsHelpText": "Inclure les saisies manuelles effectuées dans {appName}", @@ -509,7 +509,7 @@ "DeleteSelectedDownloadClients": "Supprimer le(s) client(s) de téléchargement", "DeleteSelectedDownloadClientsMessageText": "Voulez-vous vraiment supprimer {count} client(s) de téléchargement sélectionné(s) ?", "StopSelecting": "Effacer la sélection", - "UpdateAvailableHealthCheckMessage": "Une nouvelle mise à jour est disponible", + "UpdateAvailableHealthCheckMessage": "Une nouvelle mise à jour est disponible : {version}", "AdvancedSettingsHiddenClickToShow": "Paramètres avancés masqués, cliquez pour afficher", "AdvancedSettingsShownClickToHide": "Paramètres avancés affichés, cliquez pour masquer", "AppsMinimumSeeders": "Apps avec le nombre minimum de seeders disponibles", @@ -528,7 +528,7 @@ "ConnectionLostReconnect": "{appName} essaiera de se connecter automatiquement, ou vous pouvez cliquer sur « Recharger » en bas.", "ConnectionLostToBackend": "{appName} a perdu sa connexion au backend et devra être rechargé pour fonctionner à nouveau.", "RecentChanges": "Changements récents", - "WhatsNew": "Quoi de neuf ?", + "WhatsNew": "Quoi de neuf ?", "minutes": "minutes", "DeleteAppProfileMessageText": "Voulez-vous vraiment supprimer le profil d'application « {name} » ?", "AddConnection": "Ajouter une connexion", @@ -603,7 +603,7 @@ "NoIndexerCategories": "Aucune catégorie disponible pour cet indexeur", "InvalidUILanguage": "Votre interface utilisateur est définie sur une langue non valide, corrigez-la et enregistrez vos paramètres", "DownloadClientQbittorrentSettingsContentLayout": "Disposition du contenu", - "DownloadClientQbittorrentSettingsContentLayoutHelpText": "Utiliser la disposition du contenu configurée par qBittorrent, la disposition originale du torrent ou toujours créer un sous-dossier (qBittorrent 4.3.2+)", + "DownloadClientQbittorrentSettingsContentLayoutHelpText": "Si il faut utiliser de la présentation du contenu configurée par qBittorrent, la présentation originale du torrent ou la création systématique d'un sous-dossier (qBittorrent 4.3.2+)", "DownloadClientAriaSettingsDirectoryHelpText": "Emplacement facultatif pour les téléchargements, laisser vide pour utiliser l'emplacement par défaut Aria2", "ManageClients": "Gérer les clients", "DownloadClientDelugeSettingsUrlBaseHelpText": "Ajoute un préfixe à l'URL json du déluge, voir {url}", @@ -644,7 +644,7 @@ "IndexerSettingsQueryLimitHelpText": "Le nombre de requêtes maximales tel que spécifié par l'unité respective que {appName} autorisera au site", "IndexerSettingsRssKey": "Clé RSS", "IndexerSettingsSeedRatioHelpText": "Le ratio qu'un torrent doit atteindre avant de s'arrêter, vide utilise la valeur par défaut du client de téléchargement. Le ratio doit être d'au moins 1.0 et suivre les règles des indexeurs", - "IndexerSettingsSeedTime": "Temps d'envoie", + "IndexerSettingsSeedTime": "Temps d'envoi", "IndexerTorrentSyndikatSettingsApiKeyHelpText": "Clé API du site", "BlackholeFolderHelpText": "Dossier dans lequel {appName} stockera le fichier {extension}", "DefaultCategory": "Catégorie par défaut", @@ -692,7 +692,7 @@ "IndexerSettingsCookie": "Cookie", "IndexerSettingsCookieHelpText": "Cookie du site", "IndexerSettingsSeedTimeHelpText": "Durée pendant laquelle un torrent doit être envoyé avant de s'arrêter, vide utilise la valeur par défaut du client de téléchargement", - "IndexerSettingsSeedRatio": "Ratio d'envoie", + "IndexerSettingsSeedRatio": "Ratio d'envoi", "IndexerSettingsVipExpiration": "Expiration de la carte VIP", "SecretToken": "Jeton secret", "TorrentBlackholeSaveMagnetFiles": "Enregistrer les fichiers magnétiques", @@ -749,5 +749,63 @@ "IndexerMTeamTpSettingsApiKeyHelpText": "Clé API du site (trouvée dans le panneau de configuration utilisateur => Sécurité => Laboratoire)", "IndexerMTeamTpSettingsFreeleechOnlyHelpText": "Rechercher uniquement les versions freeleech", "NotificationsTelegramSettingsIncludeAppName": "Inclure {appName} dans le Titre", - "NotificationsTelegramSettingsIncludeAppNameHelpText": "Préfixer éventuellement le titre du message par {appName} pour différencier les notifications des différentes applications" + "NotificationsTelegramSettingsIncludeAppNameHelpText": "Préfixer éventuellement le titre du message par {appName} pour différencier les notifications des différentes applications", + "OverrideAndAddToDownloadClient": "Remplacer et ajouter à la file d'attente de téléchargement", + "OverrideGrabModalTitle": "Remplacer et récupérer - {title}", + "PrioritySettings": "Priorité : {priority}", + "SelectDownloadClientModalTitle": "{modalTitle} – Sélectionnez le client de téléchargement", + "IndexerGazelleGamesSettingsFreeleechOnlyHelpText": "Rechercher les publications freeleech uniquement", + "ProxyValidationBadRequest": "Échec du test du proxy. Code d'état : {statusCode}", + "ProxyValidationUnableToConnect": "Impossible de se connecter à l'indexeur : {exceptionMessage}. Vérifiez le journal pour plus de détails sur cette erreur", + "Default": "Par défaut", + "GrabRelease": "Saisir Release", + "ManualGrab": "Saisie manuelle", + "Open": "Ouvrir", + "Any": "Tous", + "BuiltIn": "Intégré", + "Script": "Script", + "InfoUrl": "URL d'informations", + "Redirected": "Rediriger", + "PublishedDate": "Date de publication", + "AllSearchResultsHiddenByFilter": "Tous les résultats sont masqués par le filtre appliqué.", + "AverageGrabs": "Prises moyennes", + "AverageQueries": "Requêtes moyennes", + "PackageVersionInfo": "{packageVersion} par {packageAuthor}", + "HealthMessagesInfoBox": "Vous pouvez trouver plus d'informations sur la cause de ces messages de contrôle de santé en cliquant sur le lien wiki (icône de livre) à la fin de la ligne, ou en vérifiant vos [journaux]({link}). Si vous rencontrez des difficultés pour interpréter ces messages, vous pouvez contacter notre support, via les liens ci-dessous.", + "LogSizeLimit": "Limite de taille du journal", + "LogSizeLimitHelpText": "Taille maximale du fichier journal en Mo avant archivage. La valeur par défaut est de 1 Mo.", + "IndexerAvistazSettingsFreeleechOnlyHelpText": "Rechercher les publications freeleech uniquement", + "IndexerAvistazSettingsUsernameHelpText": "Nom d'utilisateur du site", + "DockerUpdater": "Mettez à jour le conteneur Docker pour recevoir la mise à jour", + "Download": "Téléchargement", + "ErrorRestoringBackup": "Erreur lors de la restauration de la sauvegarde", + "ExternalUpdater": "{appName} est configuré pour utiliser un mécanisme de mise à jour externe", + "FailedToFetchUpdates": "Échec de la récupération des mises à jour", + "LogFilesLocation": "Les fichiers journaux sont situés dans : {location}", + "Logout": "Se déconnecter", + "NoEventsFound": "Aucun événement trouvé", + "RestartReloadNote": "Remarque : {appName} redémarrera et rechargera automatiquement l'interface utilisateur pendant le processus de restauration.", + "TheLogLevelDefault": "Le niveau de journalisation est par défaut à « Information » et peut être modifié dans les [paramètres généraux](/settings/general)", + "UpdateAppDirectlyLoadError": "Impossible de mettre à jour directement {appName},", + "UpdaterLogFiles": "Journaux du programme de mise à jour", + "WouldYouLikeToRestoreBackup": "Souhaitez-vous restaurer la sauvegarde « {name} » ?", + "AptUpdater": "Utiliser apt pour installer la mise à jour", + "Install": "Installer", + "InstallLatest": "Installer la dernière", + "InstallMajorVersionUpdateMessageLink": "Veuillez consulter [{domain}]({url}) pour plus d'informations.", + "InstallMajorVersionUpdate": "Installer la mise à jour", + "InstallMajorVersionUpdateMessage": "Cette mise à jour installera une nouvelle version majeure et pourrait ne pas être compatible avec votre système. Êtes-vous sûr de vouloir installer cette mise à jour ?", + "FailedToFetchSettings": "Échec de la récupération des paramètres", + "IndexerSettingsPreferMagnetUrlHelpText": "Si activé, cet indexeur privilégiera si possible l'usage de liens de type magnet aux liens torrent", + "PreferMagnetUrl": "Privilégier les liens de type magnet", + "IndexerAvistazSettingsPidHelpText": "PID de la page Mon compte ou Mon profil", + "IndexerAvistazSettingsPasswordHelpText": "Mot de passe du site", + "PreferMagnetUrlHelpText": "Si activé, cet indexeur privilégiera si possible l'usage de liens de type magnet aux liens torrent", + "PreviouslyInstalled": "Installé précédemment", + "CurrentlyInstalled": "Actuellement installé", + "IndexerSettingsPreferMagnetUrl": "URL de préférence Magnet", + "IndexerPassThePopcornSettingsGoldenPopcornOnly": "Popcorn doré uniquement", + "IndexerPassThePopcornSettingsGoldenPopcornOnlyHelpText": "Rechercher uniquement les versions Golden Popcorn", + "IndexerAvistazSettingsUsernameHelpTextWarning": "Seuls les membres de rang et supérieur peuvent utiliser l'API sur cet indexeur.", + "DownloadClientUTorrentProviderMessage": "uTorrent a un historique d'inclusion de cryptomineurs, de logiciels malveillants et de publicités. Nous vous recommandons fortement de choisir un autre client." } diff --git a/src/NzbDrone.Core/Localization/Core/he.json b/src/NzbDrone.Core/Localization/Core/he.json index 734399cc7..e46e5c09c 100644 --- a/src/NzbDrone.Core/Localization/Core/he.json +++ b/src/NzbDrone.Core/Localization/Core/he.json @@ -68,7 +68,7 @@ "UILanguageHelpTextWarning": "חובה לטעון דפדפן", "UISettings": "הגדרות ממשק המשתמש", "UnableToAddANewAppProfilePleaseTryAgain": "לא ניתן להוסיף פרופיל איכות חדש, נסה שוב.", - "UnableToLoadBackups": "לא ניתן לטעון גיבויים", + "BackupsLoadError": "לא ניתן לטעון גיבויים", "UnableToLoadTags": "לא ניתן לטעון תגים", "UnableToLoadUISettings": "לא ניתן לטעון הגדרות ממשק משתמש", "UnsavedChanges": "שינויים שלא נשמרו", @@ -122,7 +122,7 @@ "SettingsShowRelativeDates": "הצג תאריכים יחסית", "SuggestTranslationChange": "הצע שינוי בתרגום", "System": "מערכת", - "SystemTimeCheckMessage": "זמן המערכת אינו פעיל יותר מיום אחד. משימות מתוזמנות עשויות שלא לפעול כראוי עד לתיקון הזמן", + "SystemTimeHealthCheckMessage": "זמן המערכת אינו פעיל יותר מיום אחד. משימות מתוזמנות עשויות שלא לפעול כראוי עד לתיקון הזמן", "TableOptionsColumnsMessage": "בחר אילו עמודות גלויות ובאיזה סדר הן יופיעו", "TagCannotBeDeletedWhileInUse": "לא ניתן למחוק בזמן השימוש", "TagIsNotUsedAndCanBeDeleted": "לא משתמשים בתג וניתן למחוק אותו", @@ -152,7 +152,7 @@ "Grabbed": "תפס", "Grabs": "לִתְפּוֹס", "Health": "בְּרִיאוּת", - "HealthNoIssues": "אין בעיות בתצורה שלך", + "NoIssuesWithYourConfiguration": "אין בעיות בתצורה שלך", "HideAdvanced": "הסתר מתקדם", "History": "הִיסטוֹרִיָה", "HomePage": "דף הבית", @@ -371,7 +371,7 @@ "EditSyncProfile": "הוספת פרופיל סינכרון", "Notifications": "התראות", "Notification": "התראות", - "TheLatestVersionIsAlreadyInstalled": "הגרסה האחרונה של {appName} כבר מותקנת", + "OnLatestVersion": "הגרסה האחרונה של {appName} כבר מותקנת", "Remove": "לְהַסִיר", "Replace": "החלף", "AddApplication": "הוספת אפליקציה", @@ -407,5 +407,30 @@ "Publisher": "מוציא לאור", "None": "אף אחד", "AuthForm": "טפסים (דף כניסה)", - "AuthBasic": "בסיסי (חלון קופץ לדפדפן)" + "AuthBasic": "בסיסי (חלון קופץ לדפדפן)", + "IndexerHDBitsSettingsMediums": "בינוני", + "ProxyValidationBadRequest": "נכשל בדיקת ה- proxy. קוד קוד: {statusCode}", + "CustomFilter": "מסננים מותאמים אישית", + "GrabRelease": "שחרור תפוס", + "BuiltIn": "נִבנָה בְּ", + "Script": "תַסרִיט", + "PublishedDate": "תאריך פרסום", + "AddCategory": "הוסף קטגוריה", + "ActiveApps": "אפליקציות פעילות", + "ActiveIndexers": "אינדקסרים פעילים", + "AllSearchResultsHiddenByFilter": "כל התוצאות מוסתרות על ידי המסנן שהוחל", + "InstallLatest": "התקן את האחרונה", + "NoEventsFound": "לא נמצאו אירועים", + "DockerUpdater": "עדכן את מיכל העגינה לקבל את העדכון", + "Download": "הורד", + "ErrorRestoringBackup": "שגיאה בשחזור הגיבוי", + "ExternalUpdater": "{appName} מוגדר להשתמש במנגנון עדכון חיצוני", + "RestartReloadNote": "הערה: {appName} יופעל מחדש אוטומטית וטען מחדש את ממשק המשתמש במהלך תהליך השחזור.", + "UpdateAppDirectlyLoadError": "לא ניתן לעדכן את {appName} ישירות,", + "AptUpdater": "השתמש ב- apt כדי להתקין את העדכון", + "Clone": "סגור", + "CurrentlyInstalled": "מותקן כעת", + "Stats": "סטָטוּס", + "Season": "סיבה", + "Mixed": "תוקן" } diff --git a/src/NzbDrone.Core/Localization/Core/hi.json b/src/NzbDrone.Core/Localization/Core/hi.json index 1d553a4bd..c37eb20e0 100644 --- a/src/NzbDrone.Core/Localization/Core/hi.json +++ b/src/NzbDrone.Core/Localization/Core/hi.json @@ -111,7 +111,7 @@ "UnableToAddANewDownloadClientPleaseTryAgain": "नया डाउनलोड क्लाइंट जोड़ने में असमर्थ, कृपया पुनः प्रयास करें।", "UnableToAddANewIndexerPleaseTryAgain": "नया अनुक्रमणिका जोड़ने में असमर्थ, कृपया पुनः प्रयास करें।", "UnableToAddANewIndexerProxyPleaseTryAgain": "नया अनुक्रमणिका जोड़ने में असमर्थ, कृपया पुनः प्रयास करें।", - "UnableToLoadBackups": "बैकअप लोड करने में असमर्थ", + "BackupsLoadError": "बैकअप लोड करने में असमर्थ", "NoTagsHaveBeenAddedYet": "अभी तक कोई टैग नहीं जोड़े गए हैं", "Reddit": "reddit", "UpdateMechanismHelpText": "रेडर के बिल्ट इन अपडेटर या स्क्रिप्ट का उपयोग करें", @@ -154,7 +154,7 @@ "Source": "स्रोत", "StartupDirectory": "स्टार्टअप निर्देशिका", "SuggestTranslationChange": "अनुवाद परिवर्तन का सुझाव दें", - "SystemTimeCheckMessage": "सिस्टम का समय 1 दिन से अधिक बंद है। जब तक समय सही नहीं होगा तब तक शेड्यूल किए गए कार्य सही तरीके से नहीं चल सकते हैं", + "SystemTimeHealthCheckMessage": "सिस्टम का समय 1 दिन से अधिक बंद है। जब तक समय सही नहीं होगा तब तक शेड्यूल किए गए कार्य सही तरीके से नहीं चल सकते हैं", "TableOptionsColumnsMessage": "चुनें कि कौन से कॉलम दिखाई दे रहे हैं और वे किस क्रम में दिखाई देते हैं", "TagsSettingsSummary": "सभी टैग देखें और उनका उपयोग कैसे किया जाता है। अप्रयुक्त टैग को हटाया जा सकता है", "TestAll": "सभी का परीक्षण करें", @@ -260,7 +260,7 @@ "Grabbed": "पकड़ा", "Grabs": "लपकना", "Health": "स्वास्थ्य", - "HealthNoIssues": "आपके कॉन्फ़िगरेशन के साथ कोई समस्या नहीं है", + "NoIssuesWithYourConfiguration": "आपके कॉन्फ़िगरेशन के साथ कोई समस्या नहीं है", "HideAdvanced": "उन्नत छिपाएँ", "History": "इतिहास", "HomePage": "मुख पृष्ठ", @@ -328,7 +328,7 @@ "LastExecution": "अंतिम निष्पादन", "Queued": "कतारबद्ध", "Remove": "हटाना", - "TheLatestVersionIsAlreadyInstalled": "रेडर का नवीनतम संस्करण पहले से ही स्थापित है", + "OnLatestVersion": "रेडर का नवीनतम संस्करण पहले से ही स्थापित है", "Replace": "बदलने के", "More": "अधिक", "DeleteSelectedDownloadClients": "डाउनलोड क्लाइंट हटाएं", @@ -354,5 +354,24 @@ "None": "कोई नहीं", "ResetAPIKeyMessageText": "क्या आप वाकई अपनी API कुंजी को रीसेट करना चाहते हैं?", "IndexerHDBitsSettingsMediums": "मध्यम", - "CustomFilter": "कस्टम फ़िल्टर" + "CustomFilter": "कस्टम फ़िल्टर", + "ProxyValidationBadRequest": "प्रॉक्सी का परीक्षण करने में विफल। स्थिति कोड: {statusCode}", + "GrabRelease": "पकड़ो रिलीज", + "BuiltIn": "में निर्मित", + "Script": "लिपि", + "PublishedDate": "प्रकाशित तिथि", + "AllSearchResultsHiddenByFilter": "सभी परिणाम लागू फ़िल्टर द्वारा छिपे हुए हैं", + "AptUpdater": "अद्यतन स्थापित करने के लिए उपयुक्त का उपयोग करें", + "DockerUpdater": "अपडेट प्राप्त करने के लिए docker कंटेनर को अपडेट करें", + "Download": "डाउनलोड", + "ErrorRestoringBackup": "बैकअप बहाल करने में त्रुटि", + "NoEventsFound": "कोई घटना नहीं मिली", + "RestartReloadNote": "नोट: रैडियर स्वचालित रूप से पुनः आरंभ करेगा और पुनर्स्थापना प्रक्रिया के दौरान UI को फिर से लोड करेगा।", + "UpdateAppDirectlyLoadError": "सीधे {appName} अद्यतन करने में असमर्थ,", + "InstallLatest": "नवीनतम स्थापित करें", + "Clone": "बंद करे", + "CurrentlyInstalled": "वर्तमान में स्थापित है", + "Mixed": "फिक्स्ड", + "Season": "कारण", + "Stats": "स्थिति" } diff --git a/src/NzbDrone.Core/Localization/Core/hr.json b/src/NzbDrone.Core/Localization/Core/hr.json index 438337a51..52e5603e9 100644 --- a/src/NzbDrone.Core/Localization/Core/hr.json +++ b/src/NzbDrone.Core/Localization/Core/hr.json @@ -174,5 +174,43 @@ "DisabledForLocalAddresses": "Onemogućeno za Lokalne Adrese", "ResetAPIKeyMessageText": "Jeste li sigurni da želite resetirati vaš API Ključ?", "AuthBasic": "Osnovno (Skočni prozor preglednika)", - "AuthForm": "Forme (Login Stranica)" + "AuthForm": "Forme (Login Stranica)", + "Id": "ID", + "CountApplicationsSelected": "{count} Kolekcija odabrano", + "IndexerHDBitsSettingsCodecs": "Kodek", + "Directory": "Direktorij", + "BuiltIn": "Ugrađeno", + "Redirected": "Preusmjeri", + "AllSearchResultsHiddenByFilter": "Svi rezultati su skriveni zbog primjenjenog filtera", + "ApplyChanges": "Primjeni Promjene", + "ApiKeyValidationHealthCheckMessage": "Molimo ažuriraj svoj API ključ da ima barem {length} znakova. Ovo možeš uraditi u postavkama ili konfiguracijskoj datoteci", + "AppUpdated": "{appName} Ažuriran", + "AuthenticationRequired": "Potrebna Autentikacija", + "AddConnection": "Dodaj vezu", + "AddDownloadClientImplementation": "Dodaj Klijenta za Preuzimanje- {implementationName}", + "AddIndexerImplementation": "Dodaj Indexer - {implementationName}", + "AuthenticationRequiredPasswordConfirmationHelpTextWarning": "Potvrdi novu lozinku", + "AuthenticationRequiredPasswordHelpTextWarning": "Unesi novu lozinku", + "AddConnectionImplementation": "Dodaj Vezu - {implementationName}", + "Any": "BIlo koji", + "AppUpdatedVersion": "{appName} je ažuriran na verziju '{version}', kako bi najnovije promjene bile aktivne potrebno je ponovno učitati {appName}", + "AuthenticationMethod": "Metoda Autentikacije", + "AuthenticationRequiredUsernameHelpTextWarning": "Unesi novo korisničko ime", + "AuthenticationMethodHelpTextWarning": "Molimo odaberi ispravnu metodu autentikacije", + "AuthenticationRequiredWarning": "Kako bi se spriječio udaljeni pristup bez autentikacije, {appName} sad zahtjeva da autentikacija bude omogućena. Izborno se može onemogućiti autentikacija s lokalnih adresa.", + "UnableToAddANewIndexerPleaseTryAgain": "Neuspješno dodavanje novog indexera, molimo pokušaj ponovno.", + "UnableToAddANewNotificationPleaseTryAgain": "Neuspješno dodavanje nove obavijesti, molimo pokušaj ponovno.", + "UnableToAddANewDownloadClientPleaseTryAgain": "Nesupješno dodavanje klijenta za preuzimanje, molimo pokušaj ponovno.", + "EditDownloadClientImplementation": "Dodaj Klijenta za Preuzimanje- {implementationName}", + "EditConnectionImplementation": "Dodaj Vezu - {implementationName}", + "UnableToAddANewIndexerProxyPleaseTryAgain": "Neuspješno dodavanje novog indexera, molimo pokušaj ponovno.", + "AddApplicationImplementation": "Dodaj Vezu - {implementationName}", + "UnableToAddANewAppProfilePleaseTryAgain": "Neuspješno dodavanje novog profila kvalitete, molimo pokušaj ponovno.", + "EditIndexerImplementation": "Dodaj Indexer - {implementationName}", + "AddIndexerProxyImplementation": "Dodaj Indexer - {implementationName}", + "UnableToAddANewApplicationPleaseTryAgain": "Neuspješno dodavanje nove obavijesti, molimo pokušaj ponovno.", + "EditApplicationImplementation": "Dodaj Vezu - {implementationName}", + "AptUpdater": "Koristi apt kako bi instalirao ažuriranje", + "EditIndexerProxyImplementation": "Dodaj Indexer - {implementationName}", + "Clone": "Zatvori" } diff --git a/src/NzbDrone.Core/Localization/Core/hu.json b/src/NzbDrone.Core/Localization/Core/hu.json index 2193e433c..ea8d13566 100644 --- a/src/NzbDrone.Core/Localization/Core/hu.json +++ b/src/NzbDrone.Core/Localization/Core/hu.json @@ -78,7 +78,7 @@ "Actions": "Teendők", "History": "Előzmény", "HideAdvanced": "Haladó Elrejtése", - "HealthNoIssues": "Nincs hiba a konfigurációval", + "NoIssuesWithYourConfiguration": "Nincs hiba a konfigurációval", "Health": "Egészség", "GeneralSettingsSummary": "Port, SSL, felhasználónév / jelszó, proxy, elemzések, és frissítések", "ForMoreInformationOnTheIndividualDownloadClients": "Ha többet szeretnél megtudni a különböző letöltési kliensekről, kattints az információs gombokra.", @@ -224,7 +224,7 @@ "UnableToLoadHistory": "Nem sikerült betölteni az előzményeket", "UnableToLoadGeneralSettings": "Nem sikerült betölteni az általános beállításokat", "DownloadClientsLoadError": "Nem sikerült betölteni a letöltőkliens(eke)t", - "UnableToLoadBackups": "Biztonsági mentés(ek) betöltése sikertelen", + "BackupsLoadError": "Biztonsági mentés(ek) betöltése sikertelen", "UnableToAddANewNotificationPleaseTryAgain": "Nem lehet új értesítést hozzáadni, próbálkozz újra.", "UnableToAddANewIndexerPleaseTryAgain": "Nem lehet új indexert hozzáadni, próbálkozz újra.", "UnableToAddANewDownloadClientPleaseTryAgain": "Nem lehet új letöltőklienst hozzáadni, próbálkozz újra.", @@ -248,7 +248,7 @@ "TagIsNotUsedAndCanBeDeleted": "A címke nincs használatban, törölhető", "TagCannotBeDeletedWhileInUse": "Használat közben nem törölhető", "TableOptionsColumnsMessage": "Válasszd ki, mely oszlopok legyenek láthatóak, és milyen sorrendben jelenjenek meg", - "SystemTimeCheckMessage": "A rendszeridő több mint 1 napja nem frissült. Előfordulhat, hogy az ütemezett feladatok az idő kijavításáig nem futnak megfelelően", + "SystemTimeHealthCheckMessage": "A rendszeridő több mint 1 napja nem frissült. Előfordulhat, hogy az ütemezett feladatok az idő kijavításáig nem futnak megfelelően", "IndexerStatusAllUnavailableHealthCheckMessage": "Az összes indexer elérhetetlen hiba miatt", "Indexers": "Indexerek", "IndexerPriorityHelpText": "Indexelő prioritás 1-től (legmagasabb) 50-ig (legalacsonyabb). Alapértelmezés: 25.", @@ -393,7 +393,7 @@ "HistoryCleanupDaysHelpTextWarning": "A kiválasztott napszámnál régebbi előzmények automatikusan törlődnek", "IndexerAlreadySetup": "Az indexelő legalább egy példánya már be van állítva", "IndexerInfo": "Indexer információ", - "IndexerNoDefCheckMessage": "Az indexereknek nincs definíciójuk, és nem működnek: {0}. Kérjük, távolítsa el és (vagy) adja hozzá újra a {appName}hoz", + "IndexerNoDefinitionCheckHealthCheckMessage": "Az indexereknek nincs definíciójuk, és nem működnek: {indexerNames}. Kérjük, távolítsa el és (vagy) adja hozzá újra a {appName}hoz", "MassEditor": "Tömeges szerkesztő", "OnApplicationUpdate": "Alkalmazásfrissítésről", "OnApplicationUpdateHelpText": "Alkalmazásfrissítésről", @@ -456,7 +456,7 @@ "AuthenticationRequired": "Azonosítás szükséges", "AuthenticationRequiredHelpText": "Módosítsa, hogy mely kérésekhez van szükség hitelesítésre. Ne változtasson, hacsak nem érti a kockázatokat.", "AuthenticationRequiredWarning": "A hitelesítés nélküli távoli hozzáférés megakadályozása érdekében a(z) {appName} alkalmazásnak engedélyeznie kell a hitelesítést. Opcionálisan letilthatja a helyi címekről történő hitelesítést.", - "TheLatestVersionIsAlreadyInstalled": "A {appName} legújabb verziója már telepítva van", + "OnLatestVersion": "A {appName} legújabb verziója már telepítva van", "Remove": "Eltávolítás", "Replace": "Kicserél", "ApplicationURL": "Alkalmazás URL", @@ -570,5 +570,32 @@ "CustomFilter": "Egyedi Szűrők", "DownloadClientDownloadStationSettingsDirectoryHelpText": "Opcionális megosztott mappa a letöltések elhelyezéséhez, hagyja üresen az alapértelmezett Download Station hely használatához", "DownloadClientRTorrentSettingsDirectoryHelpText": "Választható hely a letöltések elhelyezéséhez, hagyja üresen az alapértelmezett Aria2 hely használatához", - "DownloadClientSettingsUseSslHelpText": "Biztonságos kapcsolat használata, amikor a(z) {clientName} szolgáltatással csatlakozik" + "DownloadClientSettingsUseSslHelpText": "Biztonságos kapcsolat használata, amikor a(z) {clientName} szolgáltatással csatlakozik", + "GrabRelease": "Release megragadása", + "ManualGrab": "Megfog", + "PrioritySettings": "Prioritás: {priority}", + "ProxyValidationBadRequest": "Proxy tesztelése sikertelen. Állapotkód: {statusCode}", + "Default": "Alapértelmezett", + "BuiltIn": "Beépített", + "Script": "Szkript", + "Any": "Bármi", + "PublishedDate": "Közzététel dátuma", + "Redirected": "Átirányítás", + "AllSearchResultsHiddenByFilter": "Az alkalmazott szűrők miatt, az összes keresési eredmény rejtve marad", + "HealthMessagesInfoBox": "Az állapotfelmérés okáról további információkat találhat, ha a sor végén található wikilinkre (könyv ikonra) kattint, vagy megnézi [logs] ({link}). Ha nehézségei vannak ezen üzenetek értelmezése során, forduljon ügyfélszolgálatunkhoz az alábbi linkeken.", + "AptUpdater": "A frissítés telepítéséhez használja az apt-t", + "DockerUpdater": "Frissítse a docker-tárolót a frissítés fogadásához", + "Download": "Letöltés", + "ErrorRestoringBackup": "Hiba a biztonsági mentés visszaállításakor", + "ExternalUpdater": "A {appName} egy külső frissítési mechanizmus használatára van konfigurálva", + "FailedToFetchUpdates": "Nem sikerült lekérni a frissítéseket", + "LogFilesLocation": "A naplófájlok itt találhatók: {location}", + "Logout": "Kijelentkezés", + "NoEventsFound": "Nem található események", + "RestartReloadNote": "Megjegyzés: A {appName} automatikusan újraindítja és újratölti a felületet a visszaállítási folyamatban.", + "UpdateAppDirectlyLoadError": "Nem lehetséges közvetlenül frissíteni a {appName}-t", + "WouldYouLikeToRestoreBackup": "Szeretné visszaállítani a(z) „{name}” biztonsági másolatot?", + "InstallLatest": "Legfrissebb telepítése", + "CurrentlyInstalled": "Jelenleg telepítve", + "PreviouslyInstalled": "Korábban telepítve" } diff --git a/src/NzbDrone.Core/Localization/Core/id.json b/src/NzbDrone.Core/Localization/Core/id.json index 659fdcc29..6adb15878 100644 --- a/src/NzbDrone.Core/Localization/Core/id.json +++ b/src/NzbDrone.Core/Localization/Core/id.json @@ -1,7 +1,7 @@ { "ApplicationURL": "URL Aplikasi", "ApplicationUrlHelpText": "URL eksternal aplikasi termasuk http(s)://, port, dan dasar URL", - "AddDownloadClient": "Tambahkan Klien Pengunduhan", + "AddDownloadClient": "Tambahkan Download Client", "Added": "Ditambahkan", "AddIndexer": "Tambahkan Pengindeks", "Age": "Usia", @@ -81,5 +81,13 @@ "AuthenticationRequiredPasswordConfirmationHelpTextWarning": "Konfirmasi sandi baru", "days": "hari", "minutes": "menit", - "Link": "Tautan" + "Link": "Tautan", + "Id": "ID", + "IndexerHDBitsSettingsCodecs": "Codec", + "ProxyValidationBadRequest": "Gagal menguji proxy. Kode Status: {statusCode}", + "AllSearchResultsHiddenByFilter": "Seluruh hasil disembunyikan karena penyaringan yang diterapkan", + "AptUpdater": "Gunakan apt untuk memasang pembaruan", + "Clone": "Tutup", + "EnableSSL": "Aktifkan RSS", + "CurrentlyInstalled": "Saat Ini Terpasang" } diff --git a/src/NzbDrone.Core/Localization/Core/is.json b/src/NzbDrone.Core/Localization/Core/is.json index b871e0415..e944179a8 100644 --- a/src/NzbDrone.Core/Localization/Core/is.json +++ b/src/NzbDrone.Core/Localization/Core/is.json @@ -48,7 +48,7 @@ "Details": "Upplýsingar", "PortNumber": "Portnúmer", "Uptime": "Spenntur", - "SystemTimeCheckMessage": "Slökkt er á tíma kerfisins meira en 1 dag. Skipulögð verkefni ganga kannski ekki rétt fyrr en tíminn er leiðréttur", + "SystemTimeHealthCheckMessage": "Slökkt er á tíma kerfisins meira en 1 dag. Skipulögð verkefni ganga kannski ekki rétt fyrr en tíminn er leiðréttur", "TableOptions": "Borðvalkostir", "TableOptionsColumnsMessage": "Veldu hvaða dálkar eru sýnilegir og í hvaða röð þeir birtast", "TagCannotBeDeletedWhileInUse": "Ekki er hægt að eyða meðan hún er í notkun", @@ -64,7 +64,7 @@ "Torrents": "Flæði", "Type": "Tegund", "UnableToAddANewApplicationPleaseTryAgain": "Ekki er hægt að bæta við nýrri tilkynningu. Reyndu aftur.", - "UnableToLoadBackups": "Ekki er hægt að hlaða afrit", + "BackupsLoadError": "Ekki er hægt að hlaða afrit", "DownloadClientsLoadError": "Ekki er hægt að hlaða niður viðskiptavinum", "UnableToLoadGeneralSettings": "Ekki er hægt að hlaða almennar stillingar", "UnableToLoadHistory": "Ekki er hægt að hlaða sögu", @@ -219,7 +219,7 @@ "EnableAutomaticSearchHelpText": "Verður notað þegar sjálfvirkar leitir eru framkvæmdar í HÍ eða af {appName}", "Filter": "Sía", "Fixed": "Fastur", - "HealthNoIssues": "Engin vandamál með stillingar þínar", + "NoIssuesWithYourConfiguration": "Engin vandamál með stillingar þínar", "Host": "Gestgjafi", "Hostname": "Gestgjafanafn", "IllRestartLater": "Ég byrja aftur seinna", @@ -329,7 +329,7 @@ "NextExecution": "Næsta framkvæmd", "Remove": "Fjarlægðu", "Replace": "Skipta um", - "TheLatestVersionIsAlreadyInstalled": "Nýjasta útgáfan af {appName} er þegar uppsett", + "OnLatestVersion": "Nýjasta útgáfan af {appName} er þegar uppsett", "ApplyTagsHelpTextAdd": "Bæta við: Bættu merkjum við núverandi lista yfir merki", "ApplyTagsHelpTextHowToApplyApplications": "Hvernig á að setja merki á völdu kvikmyndirnar", "ApplyTagsHelpTextHowToApplyIndexers": "Hvernig á að setja merki á völdu kvikmyndirnar", @@ -355,5 +355,25 @@ "ResetAPIKeyMessageText": "Ertu viss um að þú viljir endurstilla API lykilinn þinn?", "RestartProwlarr": "Endurræstu {appName}", "IndexerHDBitsSettingsMediums": "Miðlungs", - "CustomFilter": "Sérsniðin síur" + "CustomFilter": "Sérsniðin síur", + "ProxyValidationBadRequest": "Mistókst að prófa umboðsmann. Stöðukóði: {statusCode}", + "GrabRelease": "Grípa losun", + "BuiltIn": "Innbyggð", + "Script": "Handrit", + "PublishedDate": "Útgáfudagur", + "AllSearchResultsHiddenByFilter": "Allar niðurstöður eru faldar af beittu síunni", + "AptUpdater": "Notaðu apt til að setja uppfærsluna upp", + "DockerUpdater": "uppfærðu bryggjugáminn til að fá uppfærsluna", + "Download": "Sækja", + "ErrorRestoringBackup": "Villa við að endurheimta afrit", + "ExternalUpdater": "{appName} er stilltur til að nota ytri uppfærslu", + "RestartReloadNote": "Athugið: {appName} mun sjálfkrafa endurræsa og endurhlaða notendaviðmiðið meðan á endurreisnarferlinu stendur.", + "UpdateAppDirectlyLoadError": "Ekki er hægt að uppfæra {appName} beint,", + "NoEventsFound": "Engir viðburðir fundust", + "InstallLatest": "Settu upp nýjustu", + "Clone": "Lokaðu", + "CurrentlyInstalled": "Nú sett upp", + "Stats": "Staða", + "Mixed": "Fastur", + "Season": "Ástæða" } diff --git a/src/NzbDrone.Core/Localization/Core/it.json b/src/NzbDrone.Core/Localization/Core/it.json index 5eae433a0..b9942e1b1 100644 --- a/src/NzbDrone.Core/Localization/Core/it.json +++ b/src/NzbDrone.Core/Localization/Core/it.json @@ -5,11 +5,11 @@ "TagsSettingsSummary": "Vedi tutte le etichette e come vengono utilizzate. Le etichette non utilizzate possono essere rimosse", "SetTags": "Imposta Etichette", "SelectAll": "Seleziona Tutto", - "Scheduled": "Programmato", + "Scheduled": "Pianificato", "ReleaseBranchCheckOfficialBranchMessage": "La versione {0} non è una versione valida per le release di {appName}, non riceverai aggiornamenti", "ProxyResolveIpHealthCheckMessage": "Impossibile risolvere l'indirizzo IP per l'Host Configurato del Proxy {proxyHostName}", - "NoChanges": "Nessuna Modifica", - "NoChange": "Nessuna Modifica", + "NoChanges": "Nessun Cambiamento", + "NoChange": "Nessun Cambio", "LastWriteTime": "Orario di Ultima Scrittura", "Indexer": "Indicizzatore", "HideAdvanced": "Nascondi Avanzate", @@ -35,8 +35,8 @@ "ShowAdvanced": "Mostra Avanzate", "Settings": "Impostazioni", "Security": "Sicurezza", - "Search": "Cerca", - "SaveChanges": "Salva Modifiche", + "Search": "Ricerca", + "SaveChanges": "Salva Cambiamenti", "RestoreBackup": "Ripristina Backup", "ReleaseStatus": "Stato Release", "Refresh": "Aggiorna", @@ -46,7 +46,7 @@ "Proxy": "Proxy", "Protocol": "Protocollo", "Options": "Opzioni", - "MoreInfo": "Maggiori Info", + "MoreInfo": "Ulteriori Informazioni", "Logging": "Logging", "LogFiles": "File di Log", "Language": "Lingua", @@ -91,15 +91,15 @@ "CertificateValidation": "Convalida del Certificato", "Cancel": "Annulla", "BypassProxyForLocalAddresses": "Evita il Proxy per gli Indirizzi Locali", - "Branch": "Ramo", + "Branch": "Branca", "BindAddressHelpText": "Indirizzi IP validi, localhost o '*' per tutte le interfacce", "BindAddress": "Indirizzo di Ascolto", - "Backups": "Backups", + "Backups": "Backup", "BackupRetentionHelpText": "I backup più vecchi del periodo specificato saranno cancellati automaticamente", "BackupIntervalHelpText": "Intervallo fra i backup automatici", "BackupFolderHelpText": "I percorsi relativi saranno nella cartella AppData di {appName}", "Automatic": "Automatico", - "AuthenticationMethodHelpText": "Inserisci Username e Password per accedere a {appName}", + "AuthenticationMethodHelpText": "Utilizza nome utente e password per accedere a {appName}", "Authentication": "Autenticazione", "ApplyTags": "Applica Etichette", "Apply": "Applica", @@ -109,14 +109,14 @@ "Warn": "Attenzione", "Type": "Tipo", "Title": "Titolo", - "Time": "Ora", - "TestAll": "Prova Tutti", - "Test": "Test", + "Time": "Orario", + "TestAll": "Prova Tutto", + "Test": "Prova", "TableOptionsColumnsMessage": "Scegli quali colonne rendere visibili ed il loro ordine", - "TableOptions": "Opzioni della tabella", - "SystemTimeCheckMessage": "L'orario di sistema è sbagliato di più di un giorno. Le attività pianificate potrebbero non essere eseguite correttamente fino alla correzione", + "TableOptions": "Opzioni Tabella", + "SystemTimeHealthCheckMessage": "L'orario di sistema è sbagliato di più di un giorno. Le attività pianificate potrebbero non essere eseguite correttamente fino alla correzione", "Source": "Fonte", - "Shutdown": "Spegni", + "Shutdown": "Spegnimento", "Seeders": "Seeders", "Save": "Salva", "Restart": "Riavvia", @@ -130,7 +130,7 @@ "Level": "Livello", "KeyboardShortcuts": "Scorciatoie Tastiera", "Info": "Info", - "HealthNoIssues": "La tua configurazione non presenta problemi", + "NoIssuesWithYourConfiguration": "La tua configurazione non presenta problemi", "Error": "Errore", "Enable": "Abilita", "DownloadClientSettings": "Impostazioni del Client di Download", @@ -139,15 +139,15 @@ "DeleteNotification": "Cancella Notifica", "DeleteDownloadClient": "Cancella Client di Download", "DeleteBackup": "Cancella Backup", - "DatabaseMigration": "Migrazione DB", + "DatabaseMigration": "Migrazione Database", "ConnectSettings": "Impostazioni Collegamento", "ConnectionLost": "Connessione Persa", "Component": "Componente", "Columns": "Colonne", - "DeleteBackupMessageText": "Sei sicuro di voler cancellare il backup '{0}'?", + "DeleteBackupMessageText": "Sei sicuro di voler eliminare il backup '{name}'?", "CancelPendingTask": "Sei sicuro di voler cancellare questa operazione in sospeso?", "BranchUpdateMechanism": "Ramo utilizzato dal sistema di aggiornamento esterno", - "BranchUpdate": "Ramo da usare per aggiornare {appName}", + "BranchUpdate": "Branca da usare per aggiornare {appName}", "AddingTag": "Aggiungendo etichetta", "Password": "Password", "OnHealthIssueHelpText": "Quando c'è un problema", @@ -162,41 +162,41 @@ "SettingsEnableColorImpairedMode": "Abilità la Modalità Daltonica", "SendAnonymousUsageData": "Invia dati anonimi sull'uso", "ScriptPath": "Percorso dello script", - "RssIsNotSupportedWithThisIndexer": "RSS non è supportato con questo Indicizzatore", + "RssIsNotSupportedWithThisIndexer": "RSS non è supportato con questo indicizzatore", "Retention": "Ritenzione", "Result": "Risultato", "Restore": "Ripristina", "RestartRequiredHelpTextWarning": "Richiede il riavvio per avere effetto", "RestartProwlarr": "Riavvia {appName}", - "RestartNow": "Riavvia adesso", + "RestartNow": "Riavvia ora", "ResetAPIKey": "Resetta la Chiave API", - "Reset": "Resetta", + "Reset": "Reimposta", "RemovingTag": "Eliminando l'etichetta", "RemoveFilter": "Rimuovi filtro", "RemovedFromTaskQueue": "Rimosso dalla coda lavori", "RefreshMovie": "Aggiorna il Film", - "ReadTheWikiForMoreInformation": "Leggi la Wiki per maggiori informazioni", + "ReadTheWikiForMoreInformation": "Leggi la Wiki per più informazioni", "ProwlarrSupportsAnyIndexer": "{appName} supporta molti indicizzatori oltre a qualsiasi indicizzatore che utilizza lo standard Newznab/Torznab utilizzando \"Generic Newznab\" (per usenet) o \"Generic Torznab\" (per torrent). Cerca e seleziona il tuo indicizzatore da qua sotto.", "ProwlarrSupportsAnyDownloadClient": "{appName} supporta qualunque client di download elencato sotto.", "ProxyUsernameHelpText": "Devi inserire nome utente e password solo se richiesto. Altrimenti lascia vuoto.", - "ProxyType": "Tipo di Proxy", + "ProxyType": "Tipo Proxy", "ProxyPasswordHelpText": "Devi inserire nome utente e password solo se richiesto. Altrimenti lascia vuoto.", - "ProxyBypassFilterHelpText": "Usa ',' come separatore, e '*.' come jolly per i sottodomini", - "PortNumber": "Numero di porta", + "ProxyBypassFilterHelpText": "Usa ',' come separatore, e '*.' come wildcard per i sottodomini", + "PortNumber": "Numero Porta", "Port": "Porta", - "PendingChangesStayReview": "Rimani e rivedi modifiche", - "PendingChangesMessage": "Hai cambiamenti non salvati, sicuro di voler abbandonare la pagina?", + "PendingChangesStayReview": "Rimani e rivedi i cambiamenti", + "PendingChangesMessage": "Hai dei cambiamenti non salvati, sei sicuro di volere lasciare questa pagina?", "PendingChangesDiscardChanges": "Abbandona le modifiche ed esci", "PageSizeHelpText": "Numero di voci da mostrare in ogni pagina", "PackageVersion": "Versione del Pacchetto", - "OpenBrowserOnStart": "Apri il browser all'avvio", + "OpenBrowserOnStart": "Apri browser all'avvio", "NoUpdatesAreAvailable": "Nessun aggiornamento disponibile", "NoTagsHaveBeenAddedYet": "Nessuna etichetta è ancora stata aggiunta", "NoLogFiles": "Nessun file di log", "NoLeaveIt": "No, Lascialo", - "NoBackupsAreAvailable": "Nessun Backup disponibile", + "NoBackupsAreAvailable": "Nessun backup disponibile", "New": "Nuovo", - "Mode": "Modo", + "Mode": "Modalità", "Mechanism": "Meccanismo", "Manual": "Manuale", "MaintenanceRelease": "Release di Manutenzione: correzione di bug e altri miglioramenti. Vedi la storia dei Commit su Github per maggiori dettagli", @@ -209,8 +209,7 @@ "IgnoredAddresses": "Indirizzi Ignorati", "GeneralSettings": "Impostazioni Generali", "ForMoreInformationOnTheIndividualDownloadClients": "Per più informazioni sui singoli client di download clicca sui pulsanti info.", - "Fixed": "Fissato", - "FilterPlaceHolder": "Cerca Indicizzatori", + "FilterPlaceHolder": "Cerca indicizzatori", "ExistingTag": "Etichetta esistente", "Exception": "Eccezione", "ErrorLoadingContents": "Errore nel caricare i contenuti", @@ -219,29 +218,29 @@ "EnableInteractiveSearch": "Abilita la Ricerca Interattiva", "EnableAutomaticSearchHelpText": "Sarà usata quando la ricerca automatica è eseguita dalla l'intrfaccia o da {appName}", "EnableAutomaticSearch": "Attiva la Ricerca Automatica", - "DeleteTagMessageText": "Sei sicuro di voler eliminare l'etichetta '{0}'?", - "DeleteNotificationMessageText": "Sei sicuro di voler eliminare la notifica '{0}'?", - "DeleteDownloadClientMessageText": "Sei sicuro di voler eliminare il client di download '{0}'?", + "DeleteTagMessageText": "Sei sicuro di voler eliminare l'etichetta '{label}'?", + "DeleteNotificationMessageText": "Sei sicuro di voler eliminare la notifica '{name}'?", + "DeleteDownloadClientMessageText": "Sei sicuro di voler eliminare il client di download '{name}'?", "BeforeUpdate": "Prima dell'aggiornamento", "Usenet": "Usenet", "Uptime": "Tempo di attività", - "YesCancel": "Si, Cancella", + "YesCancel": "Sì, Cancella", "Version": "Versione", - "Username": "Nome utente", + "Username": "Nome Utente", "UseProxy": "Usa Proxy", "UrlBaseHelpText": "Per il supporto al reverse proxy, di default è vuoto", "URLBase": "Base Url", "UpdateScriptPathHelpText": "Percorso verso uno script personalizzato che prende un pacchetto di aggiornamento estratto e gestisce il resto del processo di aggiornamento", - "UpdateMechanismHelpText": "Usa il sistema di aggiornamento interno di {appName} o uno script", + "UpdateMechanismHelpText": "Usa il sistema di aggiornamento incorporato di {appName} o uno script", "UpdateAutomaticallyHelpText": "Scarica e installa automaticamente gli aggiornamenti. Sarai comunque in grado in installarli da Sistema: Aggiornamenti", - "UnsavedChanges": "Modifiche non salvate", + "UnsavedChanges": "Cambiamenti Non Salvati", "UnableToLoadUISettings": "Impossibile caricare le impostazioni interfaccia", "UnableToLoadTags": "Impossibile caricare le Etichette", "UnableToLoadNotifications": "Impossibile caricare le Notifiche", "UnableToLoadHistory": "Impossibile caricare la storia", "UnableToLoadGeneralSettings": "Impossibile caricare le impostazioni Generali", "DownloadClientsLoadError": "Impossibile caricare i client di download", - "UnableToLoadBackups": "Impossibile caricare i backup", + "BackupsLoadError": "Impossibile caricare i backup", "UnableToAddANewNotificationPleaseTryAgain": "Impossibile aggiungere una nuova notifica, riprova.", "UnableToAddANewIndexerPleaseTryAgain": "Impossibile aggiungere un nuovo Indicizzatore, riprova.", "UnableToAddANewDownloadClientPleaseTryAgain": "Impossibile aggiungere un nuovo client di download, riprova.", @@ -255,7 +254,7 @@ "TagIsNotUsedAndCanBeDeleted": "L'etichetta non è in uso e può essere eliminata", "TagCannotBeDeletedWhileInUse": "Non può essere cancellato mentre è in uso", "SuggestTranslationChange": "Suggerisci un cambio nella traduzione", - "StartupDirectory": "Cartella di avvio", + "StartupDirectory": "Cartella di Avvio", "StartTypingOrSelectAPathBelow": "Comincia a digitare o seleziona un percorso sotto", "SSLPort": "Porta SSL", "SSLCertPathHelpText": "Percorso file pfx", @@ -276,7 +275,7 @@ "EditIndexer": "Modifica Indicizzatore", "Disabled": "Disabilitato", "AutomaticSearch": "Ricerca Automatica", - "AddIndexer": "Aggiungi Indicizzatore", + "AddIndexer": "Aggiungi Indexer", "SaveSettings": "Salva Impostazioni", "OpenThisModal": "Apri questa Modale", "MovieIndexScrollTop": "Indice film: scorri in alto", @@ -291,7 +290,7 @@ "Enabled": "Abilitato", "Encoding": "Codifica", "EnableIndexer": "Abilita Indicizzatore", - "AddNewIndexer": "Aggiungi nuovo Indicizzatore", + "AddNewIndexer": "Aggiungi nuovo Indexer", "IndexerAuth": "Autenticazione dell'Indicizzatore", "AddDownloadClient": "Aggiungi Client di Download", "Category": "Categoria", @@ -300,15 +299,15 @@ "Donations": "Donazioni", "EnableRssHelpText": "Abilita feed RSS per l'Indicizzatore", "HomePage": "Pagina Iniziale", - "Id": "Id", + "Id": "ID", "IndexerHealthCheckNoIndexers": "Nessun Indicizzatore abilitato, {appName} non restituirà risultati di ricerca", "EnableRss": "Abilita RSS", "NoLinks": "Nessun Collegamento", "Rss": "RSS", "Wiki": "Wiki", - "AllIndexersHiddenDueToFilter": "Tutti gli Indicizzatori sono nascosti a causa del filtro applicato.", - "DeleteApplicationMessageText": "Sei sicuro di voler eliminare l'applicazione '{0}'?", - "DeleteIndexerProxyMessageText": "Sei sicuro di voler eliminare il proxy '{0}'?", + "AllIndexersHiddenDueToFilter": "Tutti gli Indexer sono nascosti a causa del filtro applicato.", + "DeleteApplicationMessageText": "Sei sicuro di voler eliminare l'applicazione '{name}'?", + "DeleteIndexerProxyMessageText": "Sei sicuro di voler eliminare il proxy '{name}'?", "Presets": "Preset", "SearchIndexers": "Cerca Indicizzatori", "UnableToAddANewIndexerProxyPleaseTryAgain": "Impossibile aggiungere un nuovo proxy per l'Indicizzatore, riprova.", @@ -342,8 +341,8 @@ "MappedDrivesRunningAsService": "Le unità di rete mappate non sono disponibili eseguendo come servizio di Windows. Vedere le FAQ per maggiori informazioni", "No": "No", "UnableToLoadIndexers": "Impossibile caricare gli Indicizzatori", - "Yes": "Si", - "AddIndexerProxy": "Aggiungi proxy dell'Indicizzatore", + "Yes": "Sì", + "AddIndexerProxy": "Aggiungi proxy dell'Indexer", "AudioSearch": "Ricerca Audio", "BookSearch": "Ricerca Libri", "AddRemoveOnly": "Solo Aggiunta e Rimozione", @@ -385,7 +384,7 @@ "IndexerDetails": "Dettagli dell'Indicizzatore", "IndexerInfo": "Info sull'Indicizzatore", "IndexerName": "Nome dell'Indicizzatore", - "IndexerNoDefCheckMessage": "Gli indicizzatori non hanno una definizione e non funzioneranno: {0}. Si prega di rimuoverli e/o di riaggiungerli a {appName}", + "IndexerNoDefinitionCheckHealthCheckMessage": "Gli indicizzatori non hanno una definizione e non funzioneranno: {indexerNames}. Si prega di rimuoverli e/o di riaggiungerli a {appName}", "HistoryCleanup": "Pulizia della Cronologia", "IndexerRss": "RSS dell'Indicizzatore", "IndexerSite": "Sito dell'Indicizzatore", @@ -434,10 +433,10 @@ "MinimumSeeders": "Seeder Minimi", "InstanceName": "Nome Istanza", "InstanceNameHelpText": "Nome istanza nella scheda e per il nome dell'app nel Syslog", - "ThemeHelpText": "Cambia il Tema dell'interfaccia dell’applicazione, il Tema 'Auto' userà il suo Tema di Sistema per impostare la modalità Chiara o Scura. Ispirato da {0}", + "ThemeHelpText": "Cambia il Tema dell'interfaccia dell’applicazione, il Tema 'Auto' userà il tuo Tema di Sistema per impostare la modalità Chiara o Scura. Ispirato da {inspiredBy}.", "LastDuration": "Ultima Durata", "LastExecution": "Ultima esecuzione", - "Queued": "In coda", + "Queued": "In Coda", "ApplicationLongTermStatusCheckAllClientMessage": "Tutte le app non disponibili da almeno 6 ore a causa di errori", "ApplicationLongTermStatusCheckSingleClientMessage": "Alcune app non sono disponibili da almeno 6 ore a causa di errori: {0}", "Duration": "Durata", @@ -457,7 +456,7 @@ "MappedCategories": "Categorie mappate", "Remove": "Rimuovi", "Replace": "Sostituire", - "TheLatestVersionIsAlreadyInstalled": "L'ultima versione di {appName} è già installata", + "OnLatestVersion": "L'ultima versione di {appName} è già installata", "ApplicationURL": "URL Applicazione", "ApplicationUrlHelpText": "L'URL esterno di questa applicazione, incluso http(s)://, porta e URL base", "Episode": "Episodio", @@ -466,9 +465,9 @@ "ApplyTagsHelpTextAdd": "Aggiungi: Aggiunge le etichette alla lista esistente di etichette", "ApplyTagsHelpTextHowToApplyApplications": "Come applicare etichette agli autori selezionati", "ApplyTagsHelpTextHowToApplyIndexers": "Come applicare etichette agli indicizzatori selezionati", - "CountIndexersSelected": "{0} indicizzatore(i) selezionato(i)", - "DeleteSelectedApplicationsMessageText": "Sei sicuro di voler eliminare l'indexer '{0}'?", - "DeleteSelectedDownloadClientsMessageText": "Sei sicuro di voler eliminare l'indexer '{0}'?", + "CountIndexersSelected": "{count} indicizzatore(i) selezionato(i)", + "DeleteSelectedApplicationsMessageText": "Sei sicuro di voler eliminare {count} applicazione(i) selezionata(e)?", + "DeleteSelectedDownloadClientsMessageText": "Sei sicuro di voler eliminare i '{count}' client di download selezionato/i?", "SelectIndexers": "Cerca Indicizzatori", "Track": "Traccia", "Book": "Libro", @@ -477,22 +476,21 @@ "ApplyTagsHelpTextReplace": "Sostituire: Sostituisce le etichette con quelle inserite (non inserire nessuna etichette per eliminarle tutte)", "DownloadClientPriorityHelpText": "Dai priorità a multipli Client di download. Round-Robin è usato per i client con la stessa priorità.", "DeleteSelectedDownloadClients": "Cancella i Client di Download", - "DeleteSelectedIndexersMessageText": "Sei sicuro di voler eliminare l'indexer '{0}'?", "Album": "Album", "Artist": "Artista", "Label": "Etichetta", - "More": "Di più", + "More": "Altro", "Season": "Stagione", "Year": "Anno", - "UpdateAvailableHealthCheckMessage": "É disponibile un nuovo aggiornamento", + "UpdateAvailableHealthCheckMessage": "Nuovo aggiornamento disponibile", "Author": "Autore", "ApplyChanges": "Applica Cambiamenti", "ApiKeyValidationHealthCheckMessage": "Aggiorna la tua chiave API in modo che abbia una lunghezza di almeno {length} caratteri. Puoi farlo dalle impostazioni o dal file di configurazione", - "DeleteAppProfileMessageText": "Sicuro di voler cancellare il profilo di qualità {0}", - "RecentChanges": "Cambiamenti recenti", + "DeleteAppProfileMessageText": "Sicuro di voler cancellare il profilo dell'app '{name}'?", + "RecentChanges": "Cambiamenti Recenti", "WhatsNew": "Cosa c'è di nuovo?", - "ConnectionLostReconnect": "Radarr cercherà di connettersi automaticamente, oppure clicca su ricarica qui sotto.", - "ConnectionLostToBackend": "Radarr ha perso la connessione al backend e dovrà essere ricaricato per ripristinare la funzionalità.", + "ConnectionLostReconnect": "{appName} cercherà di connettersi automaticamente, oppure clicca su ricarica qui sotto.", + "ConnectionLostToBackend": "{appName} ha perso la connessione al backend e dovrà essere ricaricato per ripristinare la funzionalità.", "minutes": "Minuti", "AddConnection": "Aggiungi Connessione", "NotificationStatusAllClientHealthCheckMessage": "Tutte le applicazioni non sono disponibili a causa di errori", @@ -520,9 +518,9 @@ "AddIndexerProxyImplementation": "Aggiungi indicizzatore - {implementationName}", "EditApplicationImplementation": "Aggiungi Condizione - {implementationName}", "CountApplicationsSelected": "{count} Collezione(i) Selezionate", - "EditConnectionImplementation": "Aggiungi Connessione - {implementationName}", - "EditDownloadClientImplementation": "Aggiungi un Client di Download - {implementationName}", - "EditIndexerImplementation": "Aggiungi indicizzatore - {implementationName}", + "EditConnectionImplementation": "Modifica Connessione - {implementationName}", + "EditDownloadClientImplementation": "Modifica Client di Download - {implementationName}", + "EditIndexerImplementation": "Modifica Indicizzatore - {implementationName}", "EditIndexerProxyImplementation": "Aggiungi indicizzatore - {implementationName}", "AdvancedSettingsShownClickToHide": "Impostazioni avanzate mostrate, clicca per nasconderle", "AdvancedSettingsHiddenClickToShow": "Impostazioni avanzate nascoste, clicca per mostrarle", @@ -532,7 +530,136 @@ "ActiveIndexers": "Indicizzatori Attivi", "IndexerBeyondHDSettingsSearchTypes": "Tipi di Ricerca", "Directory": "Cartella", - "CustomFilter": "Filtri Personalizzati", + "CustomFilter": "Filtro Personalizzato", "IndexerHDBitsSettingsCodecs": "Codec", - "IndexerHDBitsSettingsMediums": "medio" + "IndexerHDBitsSettingsMediums": "medio", + "GrabRelease": "Preleva Release", + "ProxyValidationBadRequest": "Il test del proxy è fallito. Codice Stato: {statusCode}", + "Discord": "Discord", + "Donate": "Dona", + "Destination": "Destinazione", + "DownloadClientFreeboxSettingsApiUrl": "API URL", + "DownloadClientFreeboxSettingsAppId": "ID App", + "DownloadClientFreeboxSettingsAppToken": "Token App", + "DownloadClientPneumaticSettingsNzbFolder": "Cartella Nzb", + "DownloadClientPneumaticSettingsNzbFolderHelpText": "Questa cartella dovrà essere raggiungibile da XBMC", + "DownloadClientRTorrentSettingsUrlPath": "Percorso Url", + "Default": "Predefinito", + "DownloadClientPneumaticSettingsStrmFolder": "Cartella Strm", + "IndexerDisabled": "Indexer Disattivato", + "GoToApplication": "Vai all'applicazione", + "AreYouSureYouWantToDeleteIndexer": "Sei sicuro di voler eliminare '{name}' da {appName}?", + "IndexerStatus": "Stato Indicizzatore", + "XmlRpcPath": "Percorso XML RPC", + "EditCategory": "Modifica Categoria", + "IndexerSettingsAdditionalParameters": "Parametri Addizionali", + "IndexerSettingsApiPath": "Percorso API", + "IndexerSettingsVipExpiration": "Scadenza VIP", + "DefaultCategory": "Categoria Predefinita", + "DownloadClientFloodSettingsAdditionalTags": "Tag addizionali", + "IndexerHDBitsSettingsMediumsHelpText": "Se non specificato, saranno utilizzate tutte le opzioni.", + "IndexerHDBitsSettingsOrigins": "Origini", + "IndexerHDBitsSettingsOriginsHelpText": "Se non specificato, saranno utilizzate tutte le opzioni.", + "IndexerSettingsCookie": "Cookie", + "DeleteSelectedApplications": "Elimina Applicazioni Selezionate", + "IndexerHDBitsSettingsCodecsHelpText": "Se non specificato, saranno utilizzate tutte le opzioni.", + "IndexerSettingsApiUser": "Utente API", + "PrioritySettings": "Priorità: {priority}", + "CountDownloadClientsSelected": "{count} client di download selezionato/i", + "NotificationsTelegramSettingsIncludeAppName": "Includi {appName} nel Titolo", + "Menu": "Menu", + "NoIndexersFound": "Nessun indicizzatore trovato", + "PasswordConfirmation": "Conferma Password", + "NoHistoryFound": "Nessun storico trovato", + "DeleteSelectedIndexersMessageText": "Sei sicuro di voler eliminare {count} applicazione(i) selezionata(e)?", + "UsenetBlackholeNzbFolder": "Cartella Nzb", + "VipExpiration": "Scadenza VIP", + "OverrideAndAddToDownloadClient": "Sovrascrivi e aggiungi alla coda di download", + "BasicSearch": "Ricerca basica", + "CountIndexersAvailable": "{count} indicizzatore/i disponibili", + "EditSelectedIndexers": "Modifica Indicizzatori Selezionati", + "FoundCountReleases": "Trovate {itemCount} release", + "ManageApplications": "Gestisci Applicazioni", + "ManageDownloadClients": "Gestisci Clients di Download", + "HistoryDetails": "Dettagli Storico", + "NotificationsEmailSettingsUseEncryption": "Usa Crittografia", + "SearchAllIndexers": "Cerca tutti gli indicizzatori", + "SearchCountIndexers": "Cerca {count} indicizzatore/i", + "SearchQueries": "Cerca Richieste", + "SeedRatio": "Rapporto Seed", + "TorznabUrl": "Url Torznab", + "TorrentBlackholeTorrentFolder": "Cartella Torrent", + "UseSsl": "Usa SSL", + "days": "giorni", + "IndexerCategories": "Categorie degli Indicizzatori", + "IndexerTorrentSyndikatSettingsApiKeyHelpText": "API Key Sito", + "LabelIsRequired": "Etichetta richiesta", + "NoIndexerHistory": "Nessun storico trovato per questo indicizzatore", + "RssFeed": "Feed RSS", + "AverageResponseTimesMs": "Tempo di Risposta Medio dell'Indicizzatore (ms)", + "DeleteSelectedIndexer": "Elimina Indicizzatore Selezionato", + "DisabledUntil": "Disattiva fino", + "DownloadClientDelugeSettingsUrlBaseHelpText": "Aggiungi un prefisso all'url del json di deluge, vedi {url}", + "Implementation": "Implementazione", + "ManageClients": "Gestisci Clients", + "NewznabUrl": "Url Newznab", + "NoApplicationsFound": "Nessuna applicazione trovata", + "IndexerSettingsBaseUrl": "Url Base", + "IndexerId": "ID Indicizzatore", + "NoDownloadClientsFound": "Nessun client di download trovato", + "BlackholeFolderHelpText": "Cartella nella quale {appName} salverà i file di tipo {extension}", + "DownloadClientNzbgetSettingsAddPausedHelpText": "Questa opzione richiede almeno la versione 16.0 di NzbGet", + "DownloadClientQbittorrentSettingsSequentialOrderHelpText": "Scarica in ordine sequenziale (qBittorrent 4.1.0+)", + "DownloadClientQbittorrentSettingsUseSslHelpText": "Usa una connessione sicura. Vedi Opzioni -> Web UI -> 'Usa HTTPS invece di HTTP' in qBittorrent.", + "DownloadClientRTorrentSettingsAddStopped": "Aggiungi Fermato", + "DownloadClientSettingsInitialState": "Stato Iniziale", + "DownloadClientSettingsInitialStateHelpText": "Stato iniziale per i torrent aggiunti a {clientName}", + "DownloadClientSettingsAddPaused": "Aggiungi In Pausa", + "DownloadClientSettingsUseSslHelpText": "Usa connessione sicura quando connetti a {clientName}", + "IndexerIPTorrentsSettingsCookieUserAgent": "Cookie User-Agent", + "IndexerSettingsApiPathHelpText": "Percorso API, solitamente {url}", + "IndexerSettingsBaseUrlHelpText": "Seleziona quale url base {appName} userà per le richieste al sito", + "NoIndexerCategories": "Nessuna categoria trovata per questo indicizzatore", + "SecretToken": "Secret Token", + "SeedRatioHelpText": "Il rapporto che un torrent dovrebbe raggiungere prima di essere fermato, vuoto è il predefinito dell'app", + "TotalQueries": "Totale Richieste", + "IndexerHistoryLoadError": "Errore caricando lo storico dell'indicizzatore", + "DeleteSelectedIndexers": "Elimina Indicizzatori Selezionati", + "InvalidUILanguage": "L'interfaccia è impostata in una lingua non valida, correggi e salva le tue impostazioni", + "IndexerSettingsSeedRatio": "Rapporto Seed", + "IndexerSettingsRssKey": "Chiave RSS", + "RssQueries": "Richieste RSS", + "DownloadClientQbittorrentSettingsSequentialOrder": "Ordine Sequenziale", + "External": "Esterno", + "IndexerNewznabSettingsAdditionalParametersHelpText": "Parametri Newznab addizionali", + "SelectDownloadClientModalTitle": "{modalTitle} - Seleziona Client di Download", + "DownloadClientSettingsDestinationHelpText": "Specifica manualmente la destinazione dei download, lascia vuoti per usare la predefinita", + "IndexerDownloadClientHealthCheckMessage": "Indicizzatori con client di download non validi: {indexerNames}.", + "SeedTimeHelpText": "Il rapporto che un torrent dovrebbe raggiungere prima di essere fermato, vuoto è il predefinito dell'app", + "IndexerPassThePopcornSettingsApiKeyHelpText": "API Key Sito", + "IndexerNzbIndexSettingsApiKeyHelpText": "API Key Sito", + "IndexerNewznabSettingsApiKeyHelpText": "API Key Sito", + "Fixed": "Fissato", + "Any": "Qualunque", + "BuiltIn": "Incluso", + "Script": "Script", + "InfoUrl": "URL Info", + "PublishedDate": "Data Pubblicazione", + "Redirected": "Reindirizzamento", + "AllSearchResultsHiddenByFilter": "Tutti i risultati sono nascosti dal filtro", + "PackageVersionInfo": "{packageVersion} di {packageAuthor}", + "DockerUpdater": "Aggiorna il container di docker per ricevere l'aggiornamento", + "Download": "Scarica", + "ErrorRestoringBackup": "Errore durante il ripristino del backup", + "ExternalUpdater": "{appName} è configurato per utilizzare un meccanismo di aggiornamento esterno", + "LogFilesLocation": "File di Log localizzati in: {location}", + "NoEventsFound": "Nessun evento trovato", + "RestartReloadNote": "Nota: {appName} si riavvierà automaticamente e ricaricherà l'interfaccia durante il processo di ripristino.", + "WouldYouLikeToRestoreBackup": "Vuoi ripristinare il backup '{name}'?", + "UpdateAppDirectlyLoadError": "Impossibile aggiornare {appName} direttamente,", + "AptUpdater": "Usa apt per installare l'aggiornamento", + "InstallLatest": "Installa il più recente", + "CurrentlyInstalled": "Attualmente Installato", + "PreviouslyInstalled": "Precedentemente Installato", + "Mixed": "Fissato" } diff --git a/src/NzbDrone.Core/Localization/Core/ja.json b/src/NzbDrone.Core/Localization/Core/ja.json index 6c88ce3d6..f75d051df 100644 --- a/src/NzbDrone.Core/Localization/Core/ja.json +++ b/src/NzbDrone.Core/Localization/Core/ja.json @@ -195,7 +195,7 @@ "Size": "サイズ", "Sort": "ソート", "SSLCertPath": "SSL証明書パス", - "SystemTimeCheckMessage": "システム時刻が1日以上ずれています。スケジュールされたタスクは、時間が修正されるまで正しく実行されない場合があります", + "SystemTimeHealthCheckMessage": "システム時刻が1日以上ずれています。スケジュールされたタスクは、時間が修正されるまで正しく実行されない場合があります", "Tags": "タグ", "TestAllClients": "すべてのクライアントをテストする", "Torrents": "トレント", @@ -206,7 +206,7 @@ "UnableToAddANewDownloadClientPleaseTryAgain": "新しいダウンロードクライアントを追加できません。もう一度やり直してください。", "UnableToAddANewIndexerPleaseTryAgain": "新しいインデクサーを追加できません。もう一度やり直してください。", "UnableToAddANewNotificationPleaseTryAgain": "新しい通知を追加できません。もう一度やり直してください。", - "UnableToLoadBackups": "バックアップを読み込めません", + "BackupsLoadError": "バックアップを読み込めません", "UnableToLoadHistory": "履歴を読み込めません", "UnableToLoadTags": "タグを読み込めません", "UnableToLoadUISettings": "UI設定を読み込めません", @@ -248,7 +248,7 @@ "GeneralSettings": "一般設定", "Grabbed": "掴んだ", "Health": "健康", - "HealthNoIssues": "構成に問題はありません", + "NoIssuesWithYourConfiguration": "構成に問題はありません", "HideAdvanced": "高度な非表示", "EnableInteractiveSearchHelpText": "インタラクティブ検索を使用する場合に使用されます", "Error": "エラー", @@ -329,7 +329,7 @@ "Queued": "キューに入れられました", "Remove": "削除する", "Replace": "交換", - "TheLatestVersionIsAlreadyInstalled": "{appName}の最新バージョンはすでにインストールされています", + "OnLatestVersion": "{appName}の最新バージョンはすでにインストールされています", "Track": "痕跡", "DeleteSelectedDownloadClients": "ダウンロードクライアントを削除する", "Genre": "ジャンル", @@ -355,5 +355,25 @@ "ResetAPIKeyMessageText": "APIキーをリセットしてもよろしいですか?", "RestartProwlarr": "{appName}を再起動します", "IndexerHDBitsSettingsMediums": "中", - "CustomFilter": "カスタムフィルター" + "CustomFilter": "カスタムフィルター", + "ProxyValidationBadRequest": "プロキシのテストに失敗しました。 StatusCode:{statusCode}", + "GrabRelease": "グラブリリース", + "Script": "脚本", + "BuiltIn": "ビルトイン", + "PublishedDate": "公開日", + "AllSearchResultsHiddenByFilter": "すべての結果は、適用されたフィルターによって非表示になります", + "DockerUpdater": "Dockerコンテナを更新して、更新を受信します", + "Download": "ダウンロード", + "ErrorRestoringBackup": "バックアップの復元中にエラーが発生しました", + "ExternalUpdater": "{appName}は、外部更新メカニズムを使用するように構成されています", + "NoEventsFound": "イベントが見つかりません", + "RestartReloadNote": "注:{appName}は、復元プロセス中にUIを自動的に再起動して再読み込みします。", + "UpdateAppDirectlyLoadError": "{appName}を直接更新できません。", + "AptUpdater": "aptを使用してアップデートをインストールします", + "InstallLatest": "最新のインストール", + "Clone": "閉じる", + "Mixed": "修繕", + "CurrentlyInstalled": "現在インストール中", + "Stats": "状態", + "Season": "理由" } diff --git a/src/NzbDrone.Core/Localization/Core/ko.json b/src/NzbDrone.Core/Localization/Core/ko.json index 5bc81c652..5a30e0f08 100644 --- a/src/NzbDrone.Core/Localization/Core/ko.json +++ b/src/NzbDrone.Core/Localization/Core/ko.json @@ -85,11 +85,11 @@ "UnableToAddANewAppProfilePleaseTryAgain": "새 품질 프로필을 추가 할 수 없습니다. 다시 시도하십시오.", "UnableToAddANewDownloadClientPleaseTryAgain": "새 다운로드 클라이언트를 추가 할 수 없습니다. 다시 시도하십시오.", "UnableToAddANewIndexerPleaseTryAgain": "새 인덱서를 추가 할 수 없습니다. 다시 시도하십시오.", - "UnableToLoadBackups": "백업을로드 할 수 없습니다.", + "BackupsLoadError": "백업을로드 할 수 없습니다.", "UpdateAutomaticallyHelpText": "업데이트를 자동으로 다운로드하고 설치합니다. 시스템 : 업데이트에서 계속 설치할 수 있습니다.", "RemoveFilter": "필터 제거", "Size": "크기", - "AllIndexersHiddenDueToFilter": "적용된 필터로 인해 모든 영화가 숨겨집니다.", + "AllIndexersHiddenDueToFilter": "적용된 필터로 인해 모든 인덱서가 숨겨집니다.", "Reset": "초기화", "Enable": "활성화", "Enabled": "활성화", @@ -115,7 +115,7 @@ "ErrorLoadingContents": "콘텐츠로드 오류", "Grabs": "붙잡다", "Torrent": "급류", - "Torrents": "급류", + "Torrents": "토렌트", "Type": "유형", "DeleteApplicationMessageText": "알림 '{0}'을(를) 삭제하시겠습니까?", "AuthenticationMethodHelpText": "{appName}에 접근하려면 사용자 이름과 암호가 필요합니다", @@ -186,7 +186,7 @@ "UnableToAddANewNotificationPleaseTryAgain": "새 알림을 추가 할 수 없습니다. 다시 시도하십시오.", "UnableToLoadGeneralSettings": "일반 설정을로드 할 수 없습니다.", "UnableToLoadNotifications": "알림을로드 할 수 없습니다.", - "UpdateMechanismHelpText": "{appName}의 내장 업데이트 프로그램 또는 스크립트 사용", + "UpdateMechanismHelpText": "{appName}의 내장 업데이트 도구 또는 스크립트 사용", "UpdateScriptPathHelpText": "추출 된 업데이트 패키지를 사용하고 나머지 업데이트 프로세스를 처리하는 사용자 지정 스크립트에 대한 경로", "URLBase": "URL베이스", "Usenet": "유즈넷", @@ -274,12 +274,12 @@ "UpdateStartupTranslocationHealthCheckMessage": "시작 폴더 '{startupFolder}'이 (가) App Translocation 폴더에 있으므로 업데이트를 설치할 수 없습니다.", "UrlBaseHelpText": "역방향 프록시 지원의 경우 기본값은 비어 있습니다.", "MovieIndexScrollBottom": "영화 색인 : 아래로 스크롤", - "View": "전망", + "View": "화면", "Wiki": "위키", "EditIndexer": "인덱서 편집", "Filter": "필터", "Health": "건강", - "HealthNoIssues": "구성에 문제 없음", + "NoIssuesWithYourConfiguration": "구성에 문제 없음", "Info": "정보", "KeyboardShortcuts": "키보드 단축키", "MovieIndexScrollTop": "영화 색인 : 상단 스크롤", @@ -310,7 +310,7 @@ "Seeders": "시더", "SelectAll": "모두 선택", "ShowAdvanced": "고급보기", - "SystemTimeCheckMessage": "시스템 시간이 1 일 이상 꺼져 있습니다. 예약 된 작업은 시간이 수정 될 때까지 올바르게 실행되지 않을 수 있습니다.", + "SystemTimeHealthCheckMessage": "시스템 시간이 1 일 이상 꺼져 있습니다. 예약 된 작업은 시간이 수정 될 때까지 올바르게 실행되지 않을 수 있습니다.", "TableOptions": "테이블 옵션", "TableOptionsColumnsMessage": "표시되는 열과 표시되는 순서 선택", "TagsHelpText": "일치하는 태그가 하나 이상있는 영화에 적용됩니다.", @@ -322,13 +322,13 @@ "GrabReleases": "그랩 릴리스", "NextExecution": "다음 실행", "ApplicationLongTermStatusCheckSingleClientMessage": "6 시간 이상 오류로 인해 인덱서를 사용할 수 없음 : {0}", - "ApplicationLongTermStatusCheckAllClientMessage": "6 시간 이상 오류로 인해 모든 인덱서를 사용할 수 없습니다.", + "ApplicationLongTermStatusCheckAllClientMessage": "6 시간 이상 오류로 인해 모든 어플리케이션을 사용할 수 없습니다.", "Ended": "종료", "LastDuration": "lastDuration", "LastExecution": "마지막 실행", "Queued": "대기 중", "Replace": "바꾸다", - "TheLatestVersionIsAlreadyInstalled": "최신 버전의 Whisparr가 이미 설치되어 있습니다.", + "OnLatestVersion": "최신 버전의 Whisparr가 이미 설치되어 있습니다.", "Remove": "없애다", "Genre": "장르", "ApplyTagsHelpTextAdd": "추가 : 기존 태그 목록에 태그를 추가합니다.", @@ -355,5 +355,156 @@ "StopSelecting": "선택 취소", "RestartProwlarr": "{appName} 다시 시작", "IndexerHDBitsSettingsMediums": "매질", - "CustomFilter": "사용자 지정 필터" + "CustomFilter": "사용자 지정 필터", + "GrabRelease": "그랩 릴리스", + "ProxyValidationBadRequest": "프록시를 테스트하지 못했습니다. StatusCode : {statusCode}", + "BuiltIn": "내장", + "PublishedDate": "발행일", + "AllSearchResultsHiddenByFilter": "적용된 필터에 의해 모든 결과가 숨겨집니다.", + "DockerUpdater": "Docker 컨테이너를 업데이트하여 업데이트를 받으십시오.", + "Download": "다운로드", + "ErrorRestoringBackup": "백업 복원 오류", + "ExternalUpdater": "{appName}는 외부 업데이트 메커니즘을 사용하도록 구성됩니다.", + "RestartReloadNote": "참고 : {appName}는 복원 프로세스 중에 UI를 자동으로 다시 시작하고 다시로드합니다.", + "UpdateAppDirectlyLoadError": "{appName}를 직접 업데이트 할 수 없습니다.", + "AptUpdater": "apt를 사용하여 업데이트 설치", + "ActiveIndexers": "활성 인덱서", + "AddConnectionImplementation": "연결 추가 - {implementationName}", + "AddIndexerImplementation": "인덱서 추가 - {implementationName}", + "AddIndexerProxyImplementation": "인덱서 프록시 추가 - {implementationName}", + "Any": "모두", + "AppUpdatedVersion": "{appName}이 버전 `{version}`으로 업데이트되었습니다. 최신 변경 사항을 받으려면 {appName}을 다시 로드해야 합니다", + "AddRemoveOnly": "추가 및 제거만", + "AddToDownloadClient": "다운로드 클라이언트에 릴리스 추가", + "AddedToDownloadClient": "클라이언트에 릴리스 추가됨", + "AdvancedSettingsHiddenClickToShow": "고급 설정은 숨겨져 있으며, 표시하려면 클릭하세요", + "AdvancedSettingsShownClickToHide": "고급 설정 표시, 숨기려면 클릭", + "AddDownloadClientToProwlarr": "다운로드 클라이언트를 추가하면 {appName}이 수동 검색을 수행하는 동안 UI에서 직접 릴리스를 보낼 수 있습니다.", + "AddApplication": "애플리케이션 추가", + "AddCustomFilter": "커스텀 필터 추가", + "AddIndexerProxy": "인덱서 프록시 추가", + "AppUpdated": "{appName} 업데이트", + "Application": "어플리케이션", + "AppProfileInUse": "사용중인 앱 프로필", + "AppSettingsSummary": "{appName}이 PVR 프로그램과 상호 작용하는 방식을 구성하기 위한 애플리케이션 및 설정", + "AddNewIndexer": "새로운 인덱서 추가", + "AddSyncProfile": "동기화 프로필 추가", + "ApplicationSettingsSyncRejectBlocklistedTorrentHashes": "동기화 중 차단 목록에 있는 토렌트 해시 거부", + "AddApplicationImplementation": "애플리케이션 추가 - {implementationName}", + "AddCategory": "카테고리 추가", + "AddConnection": "연결 추가", + "ActiveApps": "활성 앱", + "AddDownloadClientImplementation": "다운로드 클라이언트 추가 - {implementationName}", + "Album": "앨범", + "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "해시에 의해 토렌트가 차단된 경우 일부 인덱서의 RSS/검색 중에 토렌트가 제대로 거부되지 않을 수 있습니다. 이 기능을 활성화하면 토렌트를 가져온 후 클라이언트로 전송하기 전에 토렌트를 거부할 수 있습니다.", + "ApiKeyValidationHealthCheckMessage": "API 키를 {length}자 이상으로 업데이트하세요. 설정 또는 구성 파일을 통해 이 작업을 수행할 수 있습니다.", + "AppProfileSelectHelpText": "앱 프로필은 애플리케이션 동기화에서 RSS, 자동 검색 및 대화형 검색 설정을 제어하는 데 사용됩니다", + "EditIndexerImplementation": "인덱서 추가 - {implementationName}", + "EditDownloadClientImplementation": "다운로드 클라이언트 추가 - {implementationName}", + "Clone": "닫기", + "EditApplicationImplementation": "애플리케이션 추가 - {implementationName}", + "Season": "이유", + "EditConnectionImplementation": "연결 추가 - {implementationName}", + "EditSyncProfile": "동기화 프로필 추가", + "CurrentlyInstalled": "현재 설치됨", + "Mixed": "결정된", + "Stats": "상태", + "Applications": "어플리케이션", + "EditIndexerProxyImplementation": "인덱서 프록시 추가 - {implementationName}", + "WouldYouLikeToRestoreBackup": "'{name}' 백업을 복원하시겠습니까?", + "XmlRpcPath": "XML RPC 경로", + "UpdateAvailableHealthCheckMessage": "새 업데이트 사용 가능: {version}", + "UsenetBlackholeNzbFolder": "Nzb 폴더", + "UseSsl": "SSL 사용", + "TorrentBlackholeTorrentFolder": "토렌트 폴더", + "DownloadClientPneumaticSettingsNzbFolder": "Nzb 폴더", + "UserAgentProvidedByTheAppThatCalledTheAPI": "API를 호출한 앱에서 제공하는 사용자 에이전트", + "days": "일", + "minutes": "분", + "Author": "저작자", + "Categories": "카테고리", + "SeedRatio": "종자 비율", + "AuthenticationRequiredHelpText": "필수 인증을 요청하는 변경 사항. 위험을 이해하지 못한다면 변경하지 마세요.", + "DownloadClientFloodSettingsAdditionalTagsHelpText": "미디어의 속성을 태그로 추가합니다. 힌트는 예시입니다.", + "DownloadClientRTorrentSettingsAddStoppedHelpText": "활성화하면 rTorrent에 정지된 상태에서 토런트와 마그넷이 추가됩니다. 마그넷 파일이 손상될 수 있습니다.", + "HealthMessagesInfoBox": "행 끝에 있는 위키 링크(책 아이콘)를 클릭하거나 [로그]({link})를 확인하면 이러한 상태 점검 메시지의 원인에 대한 상세 정보를 찾을 수 있습니다. 이러한 메시지를 해석하는 데 어려움이 있는 경우 아래 링크에서 지원팀에 문의할 수 있습니다.", + "DownloadClientSettingsDestinationHelpText": "다운로드 대상을 수동으로 지정하고 기본값을 사용하려면 비워두세요.", + "DownloadClientSettingsInitialStateHelpText": "{clientName}에 추가된 토런트의 초기 상태", + "IndexerSettingsAdditionalParameters": "매개 변수 추가", + "NoEventsFound": "이벤트가 없음", + "BlackholeFolderHelpText": "{appName}가 {extension} 파일을 저장할 폴더", + "DownloadClientSettingsUrlBaseHelpText": "{clientName} url에 {url}과 같은 접두사를 추가합니다.", + "DownloadClientQbittorrentSettingsContentLayout": "콘텐츠 레이아웃", + "Install": "설치", + "AuthenticationRequiredPasswordConfirmationHelpTextWarning": "새 비밀번호 확인", + "FailedToFetchSettings": "설정을 가져오는데 실패함", + "Default": "기본값", + "Episode": "에피소드", + "AuthenticationMethod": "인증 방법", + "IndexerSettingsSeedRatio": "시드 비율", + "Destination": "대상", + "DownloadClientFreeboxSettingsAppToken": "앱 토큰", + "Logout": "로그아웃", + "DownloadClientFreeboxSettingsApiUrl": "API 주소", + "IndexerHDBitsSettingsCodecs": "코덱", + "WhatsNew": "새로운 소식?", + "DownloadClientFreeboxSettingsPortHelpText": "Freebox 인터페이스에 액세스하는 데 사용되는 포트, 기본값은 '{port}'", + "DownloadClientPneumaticSettingsStrmFolderHelpText": "이 폴더의 .strm 파일은 드론으로 가져옵니다.", + "DownloadClientQbittorrentSettingsInitialStateHelpText": "qBittorrent에 추가된 토렌트의 초기 상태입니다. 강제 토렌트는 시드 제한을 따르지 않는다는 점에 유의하세요.", + "DownloadClientRTorrentSettingsDirectoryHelpText": "다운로드를 넣을 선택 위치, 기본 rTorrent 위치를 사용하려면 비워두세요.", + "DownloadClientSettingsUseSslHelpText": "{clientName}에 연결할 때 보안 연결을 사용", + "DownloadClientTransmissionSettingsUrlBaseHelpText": "{clientName} rpc URL에 접두사를 추가합니다(예: {url}, 기본값은 '{defaultUrl}')", + "InstallMajorVersionUpdateMessageLink": "상세 내용은 [{domain}]({url})을 확인하세요.", + "ApplicationUrlHelpText": "이 애플리케이션의 외부 URL - http(s)://, port 및 URL 기반 포함", + "Theme": "테마", + "ApplicationURL": "애플리케이션 URL", + "Directory": "디렉토리", + "DownloadClientDownloadStationSettingsDirectoryHelpText": "다운로드를 넣을 공유 폴더(선택 사항). 기본 다운로드 스테이션 위치를 사용하려면 비워두세요.", + "DownloadClientFloodSettingsAdditionalTags": "추가 태그", + "DownloadClientFloodSettingsUrlBaseHelpText": "{url}와 같은 Flood API에 접두사를 추가합니다", + "DownloadClientFreeboxSettingsApiUrlHelpText": "API 버전을 사용하여 Freebox API 기본 URL을 정의하세요, 예를 들어 '{url}', 기본값은 '{defaultApiUrl}')", + "DownloadClientFreeboxSettingsAppId": "앱 ID", + "DownloadClientNzbgetSettingsAddPausedHelpText": "이 옵션을 사용하려면 최소한 NzbGet 버전 16.0이 필요합니다", + "DownloadClientPneumaticSettingsNzbFolderHelpText": "이 폴더는 XBMC에서 접근할 수 있어야 합니다.", + "DownloadClientRTorrentSettingsAddStopped": "중지됨 추가", + "Category": "카테고리", + "DownloadClientTransmissionSettingsDirectoryHelpText": "다운로드를 넣을 위치 (선택 사항), 기본 전송 위치를 사용하려면 비워두세요", + "External": "외부", + "IndexerSettingsSeedRatioHelpText": "토렌드가 멈추기 전에 도달해야 하는 비율, 비어 있을 경우 다운로드 클라이언트의 기본값을 사용합니다. 비율은 최소 1.0이어야 하며 인덱서 규칙을 따라야 합니다", + "IndexerSettingsSeedTimeHelpText": "토렌드가 중지되기 전에 시드되어야 하는 시간, 비어 있을 경우 다운로드 클라이언트의 기본값을 사용합니다", + "InvalidUILanguage": "UI가 잘못된 언어로 설정되어 있습니다, 수정하고 설정을 저장하세요", + "AuthenticationRequired": "인증 필요", + "NotificationsTelegramSettingsIncludeAppNameHelpText": "다른 애플리케이션의 알림을 구분하기 위해 메시지 제목 앞에 {appName}를 접두사로 사용 (선택 사항)", + "PackageVersionInfo": "{packageVersion} by {packageAuthor}", + "Duration": "기간", + "Script": "스크립트", + "SelectDownloadClientModalTitle": "{modalTitle} - 다운로드 클라이언트 선택", + "TheLogLevelDefault": "로그 수준의 기본값은 '정보'이며 [일반 설정](/settings/general)에서 변경할 수 있습니다", + "UpdaterLogFiles": "업데이트 도구 로그 파일", + "CountDownloadClientsSelected": "{count}개의 다운로드 클라이언트를 선택함", + "DefaultNameCopiedProfile": "{name} - 복사", + "DownloadClientDelugeSettingsUrlBaseHelpText": "deluge json url에 접두사를 추가합니다. {url}을(를) 참조하세요", + "FailedToFetchUpdates": "업데이트를 가져오는데 실패함", + "ApplyChanges": "변경 사항 적용", + "Started": "시작됨", + "Database": "데이터베이스", + "PasswordConfirmation": "비밀번호 확인", + "TorrentBlackholeSaveMagnetFiles": "마그넷 파일 저장", + "TorrentBlackholeSaveMagnetFilesExtension": "마그넷 파일 확장자 저장", + "TorrentBlackholeSaveMagnetFilesExtensionHelpText": "마그넷 링크에 사용할 확장자, 기본값은 '.magnet'입니다", + "TorrentBlackholeSaveMagnetFilesHelpText": ".torrent 파일을 사용할 수 없는 경우 마그넷 링크를 저장합니다 (다운로드 클라이언트가 파일에 저장된 마그넷을 지원하는 경우에만 유용함)", + "AuthenticationMethodHelpTextWarning": "유효한 인증 방법을 선택해주세요", + "AuthenticationRequiredPasswordHelpTextWarning": "새로운 비밀번호를 입력하세요", + "AuthenticationRequiredUsernameHelpTextWarning": "새로운 사용자이름을 입력하세요", + "AuthenticationRequiredWarning": "인증 없이 원격 액세스를 방지하기 위해 {appName}은(는) 이제 인증을 활성화해야 합니다. 선택적으로 로컬 주소에서 인증을 비활성화할 수 있습니다.", + "CountIndexersSelected": "{count}개의 인덱서를 선택함", + "Label": "라벨", + "More": "더 보기", + "Donate": "기부하기", + "Menu": "메뉴", + "DownloadClientQbittorrentSettingsContentLayoutHelpText": "qBittorrent의 구성된 콘텐츠 레이아웃을 사용할지, 토런트의 원래 레이아웃을 사용할지, 항상 하위 폴더를 생성할지(qBittorrent 4.3.2+)", + "DownloadClientSettingsAddPaused": "일시 중지 추가", + "SecretToken": "비밀 토큰", + "NoDownloadClientsFound": "다운로드 클라이언트를 찾을 수 없음", + "PrioritySettings": "우선 순위: {0}" } diff --git a/src/NzbDrone.Core/Localization/Core/nb_NO.json b/src/NzbDrone.Core/Localization/Core/nb_NO.json index abebd36fc..12e1c4557 100644 --- a/src/NzbDrone.Core/Localization/Core/nb_NO.json +++ b/src/NzbDrone.Core/Localization/Core/nb_NO.json @@ -131,11 +131,35 @@ "ConnectionLostReconnect": "Radarr vil forsøke å koble til automatisk, eller du kan klikke oppdater nedenfor.", "ConnectionLostToBackend": "Radarr har mistet tilkoblingen til baksystemet og må lastes inn på nytt for å gjenopprette funksjonalitet.", "DeleteAppProfileMessageText": "Er du sikker på at du vil slette denne forsinkelsesprofilen?", - "AddConnection": "Legg til kobling", + "AddConnection": "Legg til tilkobling", "AuthBasic": "Grunnleggende (nettleser -popup)", "AuthForm": "Skjemaer (påloggingsside)", "DisabledForLocalAddresses": "Deaktivert for lokale adresser", "ResetAPIKeyMessageText": "Er du sikker på at du vil tilbakestille API -nøkkelen din?", "CountApplicationsSelected": "{count} samling(er) valgt", - "Id": "ID" + "Id": "ID", + "UnableToAddANewNotificationPleaseTryAgain": "Ikke mulig å legge til ny betingelse, vennligst prøv igjen", + "Docker": "Docker", + "UnableToAddANewDownloadClientPleaseTryAgain": "Ikke mulig å legge til ny betingelse, vennligst prøv igjen", + "EditApplicationImplementation": "Legg til betingelse - {implementationName}", + "EditIndexerImplementation": "Legg til betingelse - {implementationName}", + "AddApplicationImplementation": "Legg til betingelse - {implementationName}", + "EditConnectionImplementation": "Legg til betingelse - {implementationName}", + "IndexerHDBitsSettingsCodecs": "Kodek", + "UnableToAddANewIndexerProxyPleaseTryAgain": "Ikke mulig å legge til ny betingelse, vennligst prøv igjen", + "Directory": "Mappe", + "UnableToAddANewIndexerPleaseTryAgain": "Ikke mulig å legge til ny betingelse, vennligst prøv igjen", + "AddConnectionImplementation": "Legg til betingelse - {implementationName}", + "AddIndexerImplementation": "Legg til betingelse - {implementationName}", + "AddIndexerProxyImplementation": "Legg til betingelse - {implementationName}", + "UnableToAddANewApplicationPleaseTryAgain": "Ikke mulig å legge til ny betingelse, vennligst prøv igjen", + "EditIndexerProxyImplementation": "Legg til betingelse - {implementationName}", + "UnableToAddANewAppProfilePleaseTryAgain": "Ikke mulig å legge til ny betingelse, vennligst prøv igjen", + "BuiltIn": "Bygget inn", + "AllSearchResultsHiddenByFilter": "Alle resultatene er skjult av det anvendte filteret", + "AptUpdater": "Bruk apt til å installere oppdateringen", + "Discord": "Discord", + "AddCustomFilter": "Legg til eget filter", + "Clone": "Lukk", + "AddDownloadClientImplementation": "Ny Nedlastingsklient - {implementationName}" } diff --git a/src/NzbDrone.Core/Localization/Core/nl.json b/src/NzbDrone.Core/Localization/Core/nl.json index 5728e3e7b..c7c2826d9 100644 --- a/src/NzbDrone.Core/Localization/Core/nl.json +++ b/src/NzbDrone.Core/Localization/Core/nl.json @@ -154,7 +154,7 @@ "Grabbed": "Opgehaalde", "Grabs": "Gegrepen", "Health": "Gezondheid", - "HealthNoIssues": "Geen problemen gevonden met uw configuratie", + "NoIssuesWithYourConfiguration": "Geen problemen gevonden met uw configuratie", "HideAdvanced": "Verberg Gevorderd", "History": "Geschiedenis", "HistoryCleanupDaysHelpText": "Zet op 0 om automatisch opschonen uit te schakelen", @@ -350,7 +350,7 @@ "SyncLevelAddRemove": "Alleen toevoegen en verwijderen: wanneer het wordt toegevoegd of verwijderd uit {appName}, wordt deze externe app bijgewerkt.", "SyncLevelFull": "Volledige synchronisatie: houdt deze app volledig gesynchroniseerd. Wijzigingen in {appName} worden vervolgens gesynchroniseerd met deze app. Elke wijziging die op afstand wordt aangebracht, wordt bij de volgende synchronisatie overschreven door {appName}.", "System": "Systeem", - "SystemTimeCheckMessage": "De systeemtijd loopt verkeerd met meer dan 1 dag. Geplande taken worden mogelijk niet goed uitgevoerd tot dit is opgelost", + "SystemTimeHealthCheckMessage": "De systeemtijd loopt verkeerd met meer dan 1 dag. Geplande taken worden mogelijk niet goed uitgevoerd tot dit is opgelost", "TableOptions": "Tabel Opties", "TableOptionsColumnsMessage": "Kies welke kolommen zichtbaar zijn en in welke volgorde", "TagCannotBeDeletedWhileInUse": "Kan niet verwijderd worden terwijl in gebruik", @@ -364,7 +364,7 @@ "TestAllApps": "Alle apps testen", "TestAllClients": "Test Alle Downloaders", "TestAllIndexers": "Test Alle Indexeerders", - "TheLatestVersionIsAlreadyInstalled": "De nieuwste versie van {appName} is al geïnstalleerd", + "OnLatestVersion": "De nieuwste versie van {appName} is al geïnstalleerd", "Time": "Tijd", "Title": "Titel", "Today": "Vandaag", @@ -386,7 +386,7 @@ "UnableToAddANewIndexerProxyPleaseTryAgain": "Kan geen nieuwe Indexeerder-proxy toevoegen. Probeer het opnieuw.", "UnableToAddANewNotificationPleaseTryAgain": "Kon geen nieuwe notificatie toevoegen, gelieve opnieuw te proberen.", "UnableToLoadAppProfiles": "Kan app-profielen niet laden", - "UnableToLoadBackups": "Kon geen veiligheidskopieën laden", + "BackupsLoadError": "Kon geen veiligheidskopieën laden", "UnableToLoadDevelopmentSettings": "Kan ontwikkelingsinstellingen niet laden", "DownloadClientsLoadError": "Downloaders kunnen niet worden geladen", "UnableToLoadGeneralSettings": "Kon Algemene instellingen niet inladen", @@ -484,5 +484,33 @@ "AuthenticationRequiredWarning": "Om toegang zonder authenticatie te voorkomen vereist {appName} nu verificatie. Je kan dit optioneel uitschakelen voor lokale adressen.", "Episode": "aflevering", "CountApplicationsSelected": "{count} Collectie(s) geselecteerd", - "BlackholeFolderHelpText": "De map waarin {appName} het {extension} bestand opslaat" + "BlackholeFolderHelpText": "De map waarin {appName} het {extension} bestand opslaat", + "GrabRelease": "Uitgave Ophalen", + "PrioritySettings": "Prioriteit: {priority}", + "Artist": "artiest", + "IndexerHDBitsSettingsMediums": "Gemiddeld", + "IndexerHDBitsSettingsCodecs": "Codec", + "Directory": "Map", + "ProxyValidationBadRequest": "Testen van proxy is mislukt. Statuscode: {statusCode}", + "CustomFilter": "Aangepaste Filters", + "Any": "Elke", + "BuiltIn": "Ingebouwd", + "Script": "Script", + "PublishedDate": "Publicatie Datum", + "Redirected": "Omleiden", + "AllSearchResultsHiddenByFilter": "Alle resultaten zijn verborgen door het toegepaste filter", + "Clone": "Kloon", + "DownloadClientSettingsUrlBaseHelpText": "Voegt een voorvoegsel toe aan de {connectionName} url, zoals {url}", + "AptUpdater": "Gebruik apt om de update te installeren", + "DockerUpdater": "Update de docker container om de update te ontvangen", + "ErrorRestoringBackup": "Fout bij het herstellen van de back-up", + "ExternalUpdater": "{appName} is geconfigureerd om een extern update mechanisme te gebruiken", + "NoEventsFound": "Geen gebeurtenissen gevonden", + "RestartReloadNote": "Aantekening: {appName} zal automatisch de Ui herstarten en herladen gedurende het herstel proces.", + "UpdateAppDirectlyLoadError": "Kan {appName} niet rechtstreeks updaten,", + "WouldYouLikeToRestoreBackup": "Wilt u de back-up {name} herstellen?", + "Download": "Downloaden", + "InstallLatest": "Installeer Nieuwste Versie", + "CurrentlyInstalled": "Momenteel Geïnstalleerd", + "Mixed": "Opgelost" } diff --git a/src/NzbDrone.Core/Localization/Core/pl.json b/src/NzbDrone.Core/Localization/Core/pl.json index f751e6515..a1e861845 100644 --- a/src/NzbDrone.Core/Localization/Core/pl.json +++ b/src/NzbDrone.Core/Localization/Core/pl.json @@ -109,7 +109,7 @@ "AuthenticationMethodHelpText": "Wymagaj nazwy użytkownika i hasła, aby uzyskać dostęp do {appName}", "BackupFolderHelpText": "Względne ścieżki będą znajdować się w katalogu AppData {appName}", "BackupRetentionHelpText": "Automatyczne kopie zapasowe starsze niż okres przechowywania zostaną automatycznie wyczyszczone", - "BindAddressHelpText": "Prawidłowy adres IP4 lub „*” dla wszystkich interfejsów", + "BindAddressHelpText": "Prawidłowy adres IP, localhost lub '*' dla wszystkich interfejsów", "BranchUpdateMechanism": "Gałąź używana przez zewnętrzny mechanizm aktualizacji", "BypassProxyForLocalAddresses": "Pomijaj serwer proxy dla adresów lokalnych", "CancelPendingTask": "Czy na pewno chcesz anulować to oczekujące zadanie?", @@ -124,9 +124,9 @@ "DatabaseMigration": "Migracja bazy danych", "DeleteApplicationMessageText": "Czy na pewno chcesz usunąć powiadomienie „{0}”?", "DeleteBackup": "Usuń kopię zapasową", - "DeleteBackupMessageText": "Czy na pewno chcesz usunąć kopię zapasową „{0}”?", + "DeleteBackupMessageText": "Czy na pewno chcesz usunąć kopię zapasową „{name}”?", "DeleteDownloadClient": "Usuń klienta pobierania", - "DeleteDownloadClientMessageText": "Czy na pewno chcesz usunąć klienta pobierania „{0}”?", + "DeleteDownloadClientMessageText": "Czy na pewno chcesz usunąć klienta pobierania „{name}”?", "DeleteNotification": "Usuń powiadomienie", "Disabled": "Wyłączone", "Docker": "Doker", @@ -150,7 +150,7 @@ "GeneralSettings": "Ustawienia główne", "GeneralSettingsSummary": "Port, SSL, nazwa użytkownika / hasło, proxy, analizy i aktualizacje", "Grabbed": "Złapał", - "HealthNoIssues": "Żadnych problemów z konfiguracją", + "NoIssuesWithYourConfiguration": "Żadnych problemów z konfiguracją", "HideAdvanced": "Ukryj zaawansowane", "History": "Historia", "Hostname": "Nazwa hosta", @@ -237,10 +237,10 @@ "SSLCertPathHelpText": "Ścieżka do pliku pfx", "SSLPort": "Port SSL", "StartTypingOrSelectAPathBelow": "Zacznij pisać lub wybierz ścieżkę poniżej", - "StartupDirectory": "Katalog startowy", + "StartupDirectory": "Katalog Startowy", "Status": "Status", "SuggestTranslationChange": "Zaproponuj zmianę tłumaczenia", - "SystemTimeCheckMessage": "Czas systemowy jest wyłączony o więcej niż 1 dzień. Zaplanowane zadania mogą nie działać poprawnie, dopóki czas nie zostanie skorygowany", + "SystemTimeHealthCheckMessage": "Czas systemowy jest wyłączony o więcej niż 1 dzień. Zaplanowane zadania mogą nie działać poprawnie, dopóki czas nie zostanie skorygowany", "Tags": "Tagi", "TagsHelpText": "Dotyczy filmów z co najmniej jednym pasującym tagiem", "TagsSettingsSummary": "Zobacz wszystkie tagi i sposób ich używania. Nieużywane tagi można usunąć", @@ -261,7 +261,7 @@ "UnableToAddANewIndexerPleaseTryAgain": "Nie można dodać nowego indeksatora, spróbuj ponownie.", "UnableToAddANewIndexerProxyPleaseTryAgain": "Nie można dodać nowego indeksatora, spróbuj ponownie.", "UnableToAddANewNotificationPleaseTryAgain": "Nie można dodać nowego powiadomienia, spróbuj ponownie.", - "UnableToLoadBackups": "Nie można załadować kopii zapasowych", + "BackupsLoadError": "Nie można załadować kopii zapasowych", "DownloadClientsLoadError": "Nie można załadować klientów pobierania", "UnableToLoadGeneralSettings": "Nie można załadować ustawień ogólnych", "UnableToLoadHistory": "Nie można załadować historii", @@ -341,7 +341,7 @@ "ApplicationLongTermStatusCheckAllClientMessage": "Wszystkie indeksatory są niedostępne z powodu awarii przez ponad 6 godzin", "Remove": "Usunąć", "Replace": "Zastąpić", - "TheLatestVersionIsAlreadyInstalled": "Najnowsza wersja {appName} jest już zainstalowana", + "OnLatestVersion": "Najnowsza wersja {appName} jest już zainstalowana", "ApplicationURL": "Link do aplikacji", "ApplicationUrlHelpText": "Zewnętrzny URL tej aplikacji zawierający http(s)://, port i adres URL", "ApplyTagsHelpTextAdd": "Dodaj: dodaj tagi do istniejącej listy tagów", @@ -382,9 +382,51 @@ "EditDownloadClientImplementation": "Dodaj klienta pobierania - {implementationName}", "Id": "Identyfikator", "AddApplicationImplementation": "Dodaj Connection - {implementationName}", - "AddIndexerImplementation": "Dodaj condition - {implementationName}", + "AddIndexerImplementation": "Dodaj indeks - {implementationName}", "AddIndexerProxyImplementation": "Dodaj condition - {implementationName}", "EditConnectionImplementation": "Dodaj Connection - {implementationName}", "EditApplicationImplementation": "Dodaj Connection - {implementationName}", - "EditIndexerImplementation": "Dodaj condition - {implementationName}" + "EditIndexerImplementation": "Dodaj condition - {implementationName}", + "IndexerHDBitsSettingsMediums": "Średni", + "EditIndexerProxyImplementation": "Dodaj condition - {implementationName}", + "Directory": "Folder", + "IndexerHDBitsSettingsCodecs": "Kodek", + "ProxyValidationBadRequest": "Nie udało się przetestować serwera proxy. StatusCode: {statusCode}", + "CustomFilter": "Filtry niestandardowe", + "GrabRelease": "Pobierz Wydanie", + "Script": "Scenariusz", + "BuiltIn": "Wbudowany", + "PublishedDate": "Data publikacji", + "AllSearchResultsHiddenByFilter": "Wszystkie wyniki są ukrywane przez zastosowany filtr", + "AppUpdated": "{appName} Zaktualizowany", + "AppUpdatedVersion": "{appName} został zaktualizowany do wersji `{version}`, by uzyskać nowe zmiany należy przeładować {appName}", + "AddCustomFilter": "Dodaj niestandardowy filtr", + "AuthenticationMethodHelpTextWarning": "Wybierz prawidłową metodę autoryzacji", + "Any": "Dowolny", + "AuthenticationMethod": "Metoda Autoryzacji", + "AuthenticationRequired": "Wymagana Autoryzacja", + "Categories": "Kategorie", + "Label": "Etykieta", + "Notification": "Powiadomienia", + "Season": "Sezon", + "Theme": "Motyw", + "Artist": "artysta", + "Album": "album", + "Connect": "Powiadomienia", + "Episode": "odcinek", + "Notifications": "Powiadomienia", + "Publisher": "Wydawca", + "Download": "Ściągnij", + "ErrorRestoringBackup": "Błąd podczas przywracania kopii zapasowej", + "ExternalUpdater": "{appName} jest skonfigurowany do korzystania z zewnętrznego mechanizmu aktualizacji", + "NoEventsFound": "Nie znaleziono wydarzeń", + "RestartReloadNote": "Uwaga: {appName} automatycznie uruchomi się ponownie i przeładuje interfejs użytkownika podczas procesu przywracania.", + "UpdateAppDirectlyLoadError": "Nie można bezpośrednio zaktualizować {appName},", + "AptUpdater": "Użyj apt, aby zainstalować aktualizację", + "DockerUpdater": "zaktualizuj kontener Dockera, aby otrzymać aktualizację", + "InstallLatest": "Zainstaluj najnowsze", + "Clone": "Zamknij", + "Stats": "Status", + "CurrentlyInstalled": "Aktualnie zainstalowane", + "Mixed": "Naprawiony" } diff --git a/src/NzbDrone.Core/Localization/Core/pt.json b/src/NzbDrone.Core/Localization/Core/pt.json index cc747f1e3..6a49c3d2c 100644 --- a/src/NzbDrone.Core/Localization/Core/pt.json +++ b/src/NzbDrone.Core/Localization/Core/pt.json @@ -21,7 +21,7 @@ "Tags": "Etiquetas", "TableOptionsColumnsMessage": "Escolha quais colunas são visíveis e em qual ordem aparecem", "TableOptions": "Opções da tabela", - "SystemTimeCheckMessage": "A hora do sistema está atrasada em mais de 1 dia. As tarefas agendadas podem não ocorrer corretamente até a hora ser corrigida", + "SystemTimeHealthCheckMessage": "A hora do sistema está atrasada em mais de 1 dia. As tarefas agendadas podem não ocorrer corretamente até a hora ser corrigida", "System": "Sistema", "Style": "Estilo", "Status": "Estado", @@ -86,7 +86,7 @@ "Host": "Anfitrião", "History": "Histórico", "HideAdvanced": "Ocultar avançado", - "HealthNoIssues": "Não há problemas com suas definições", + "NoIssuesWithYourConfiguration": "Não há problemas com suas definições", "Health": "Estado de funcionamento", "Grabbed": "Capturado", "GeneralSettingsSummary": "Porta, SSL, nome de utilizador/palavra-passe, proxy, análises e actualizações", @@ -132,7 +132,7 @@ "AuthenticationMethodHelpText": "Solicitar nome de utilizador e palavra-passe para acessar ao {appName}", "Authentication": "Autenticação", "ApplyTags": "Aplicar etiquetas", - "AppDataDirectory": "Pasta AppData", + "AppDataDirectory": "Diretório AppData", "ApiKey": "Chave da API", "AnalyticsEnabledHelpText": "Envia informações anônimas de uso e de erros aos servidores do {appName}. Isso inclui informações sobre seu browser, páginas utilizadas na WebUI do {appName}, relatórios de erros, bem como as versões do sistema operativo e da aplicação. Utilizaremos essas informações para priorizar funcionalidades e correções de bugs.", "ProxyType": "Tipo de proxy", @@ -270,7 +270,7 @@ "UnableToLoadGeneralSettings": "Não foi possível carregar as definições gerais", "DownloadClientsLoadError": "Não foi possível carregar os clientes de transferências", "UnableToAddANewDownloadClientPleaseTryAgain": "Não foi possível adicionar um novo cliente de transferências, tenta novamente.", - "UnableToLoadBackups": "Não foi possível carregar as cópias de segurança", + "BackupsLoadError": "Não foi possível carregar as cópias de segurança", "UnableToAddANewNotificationPleaseTryAgain": "Não foi possível adicionar uma nova notificação, tenta novamente.", "UISettings": "Definições da IU", "UILanguageHelpTextWarning": "É preciso reiniciar o browser", @@ -404,7 +404,7 @@ "Queued": "Em fila", "Remove": "Remover", "Replace": "Substituir", - "TheLatestVersionIsAlreadyInstalled": "A versão mais recente do {appName} já está instalada", + "OnLatestVersion": "A versão mais recente do {appName} já está instalada", "AddSyncProfile": "Adicionar Perfil de Sincronização", "AddApplication": "Adicionar Aplicação", "AddCustomFilter": "Adicionar Filtro customizado", @@ -481,5 +481,27 @@ "Directory": "Diretório", "UseSsl": "Usar SSL", "IndexerHDBitsSettingsCodecs": "Codificador", - "CustomFilter": "Filtros personalizados" + "CustomFilter": "Filtros personalizados", + "Clone": "Clonar", + "GrabRelease": "Capturar versão", + "DownloadClientSettingsUrlBaseHelpText": "Adiciona um prefixo ao URL {connectionName}, como {url}", + "ProxyValidationBadRequest": "Falha ao testar o proxy. Código de estado: {statusCode}", + "Default": "Predefinição", + "Any": "Quaisquer", + "Script": "Script", + "BuiltIn": "Incorporado", + "PublishedDate": "Data de publicação", + "AllSearchResultsHiddenByFilter": "Todos os resultados foram ocultados pelo filtro aplicado", + "BlackholeFolderHelpText": "Pasta em que {appName} guardará o ficheiro {extension}.", + "AptUpdater": "Utilize o apt para instalar a atualização", + "DockerUpdater": "atualize o contentor do Docker para receber a atualização", + "Download": "Transferência", + "ErrorRestoringBackup": "Erro ao restaurar cópia de segurança", + "ExternalUpdater": "O {appName} está definido para usar um mecanismo de atualização externo", + "NoEventsFound": "Nenhum evento encontrado", + "RestartReloadNote": "Nota: o {appName} reiniciará e recarregará automaticamente a IU durante o processo de restauração.", + "UpdateAppDirectlyLoadError": "Não foi possível atualizar o {appName} diretamente,", + "InstallLatest": "Instalar o mais recente", + "CurrentlyInstalled": "Atualmente instalado", + "Mixed": "Corrigido" } diff --git a/src/NzbDrone.Core/Localization/Core/pt_BR.json b/src/NzbDrone.Core/Localization/Core/pt_BR.json index 9f6e41108..43c1f2932 100644 --- a/src/NzbDrone.Core/Localization/Core/pt_BR.json +++ b/src/NzbDrone.Core/Localization/Core/pt_BR.json @@ -15,17 +15,17 @@ "AddToDownloadClient": "Adicionar lançamento ao cliente de download", "Added": "Adicionado", "AddedToDownloadClient": "Lançamento adicionado ao cliente", - "AddingTag": "Adicionar tag", + "AddingTag": "Adicionar etiqueta", "Age": "Tempo de vida", "Album": "Álbum", "All": "Todos", "AllIndexersHiddenDueToFilter": "Todos os indexadores estão ocultos devido ao filtro aplicado.", "Analytics": "Análises", - "AnalyticsEnabledHelpText": "Envie informações anônimas de uso e erro para os servidores do {appName}. Isso inclui informações sobre seu navegador, quais páginas da interface Web do {appName} você usa, relatórios de erros, e a versão do sistema operacional e do tempo de execução. Usaremos essas informações para priorizar recursos e correções de bugs.", + "AnalyticsEnabledHelpText": "Envie informações anônimas de uso e erro para os servidores do {appName}. Isso inclui informações sobre seu navegador, quais páginas da interface Web do {appName} você usa, relatórios de erros, a versão do sistema operacional e do tempo de execução. Usaremos essas informações para priorizar recursos e correções de bugs.", "ApiKey": "Chave da API", "ApiKeyValidationHealthCheckMessage": "Atualize sua chave de API para ter pelo menos {length} caracteres. Você pode fazer isso através das configurações ou do arquivo de configuração", "AppDataDirectory": "Diretório AppData", - "AppDataLocationHealthCheckMessage": "A atualização não será possível para evitar a exclusão de AppData na atualização", + "AppDataLocationHealthCheckMessage": "A atualização não será possível para evitar a exclusão de AppData na Atualização", "AppProfileInUse": "Perfil de aplicativo em uso", "AppProfileSelectHelpText": "Os perfis de aplicativos são usados para controlar as configurações de RSS, Pesquisa automática e Pesquisa interativa ao sincronizar o aplicativo", "AppSettingsSummary": "Aplicativos e configurações para configurar como {appName} interage com seus programas PVR", @@ -38,7 +38,7 @@ "ApplicationUrlHelpText": "A URL externa deste aplicativo, incluindo http(s)://, porta e URL base", "Applications": "Aplicativos", "Apply": "Aplicar", - "ApplyTags": "Aplicar Tags", + "ApplyTags": "Aplicar etiquetas", "Apps": "Aplicativos", "AreYouSureYouWantToDeleteCategory": "Tem certeza de que deseja excluir a categoria mapeada?", "Artist": "Artista", @@ -48,33 +48,33 @@ "AuthenticationMethodHelpText": "Exigir nome de usuário e senha para acessar o {appName}", "AuthenticationRequired": "Autenticação exigida", "AuthenticationRequiredHelpText": "Altere para quais solicitações a autenticação é necessária. Não mude a menos que você entenda os riscos.", - "AuthenticationRequiredWarning": "Para evitar o acesso remoto sem autenticação, {appName} agora exige que a autenticação esteja habilitada. Opcionalmente, você pode desabilitar a autenticação de endereços locais.", + "AuthenticationRequiredWarning": "Para evitar o acesso remoto sem autenticação, o {appName} agora exige que a autenticação esteja habilitada. Opcionalmente, você pode desabilitar a autenticação para endereços locais.", "Author": "Autor", "Automatic": "Automático", - "AutomaticSearch": "Pesquisa Automática", + "AutomaticSearch": "Pesquisa automática", "AverageResponseTimesMs": "Tempos Médios de Resposta do Indexador (ms)", "Backup": "Backup", "BackupFolderHelpText": "Os caminhos relativos estarão no diretório AppData do {appName}", "BackupIntervalHelpText": "Intervalo entre backups automáticos", "BackupNow": "Fazer backup agora", - "BackupRetentionHelpText": "Backups automáticos anteriores ao período de retenção serão limpos automaticamente", + "BackupRetentionHelpText": "Backups automáticos anteriores ao período de retenção serão excluídos automaticamente", "Backups": "Backups", "BeforeUpdate": "Antes da atualização", - "BindAddress": "Fixar endereço", + "BindAddress": "Vincular endereço", "BindAddressHelpText": "Endereço IP válido, localhost ou '*' para todas as interfaces", "Book": "Livro", "BookSearch": "Pesquisar Livro", "BookSearchTypes": "Tipos de Pesquisa de Livros", "Branch": "Ramificação", - "BranchUpdate": "Ramificação para atualização do {appName}", + "BranchUpdate": "Ramificação para atualizar o {appName}", "BranchUpdateMechanism": "Ramificação usada pelo mecanismo externo de atualização", "BypassProxyForLocalAddresses": "Ignorar proxy para endereços locais", "Cancel": "Cancelar", - "CancelPendingTask": "Tem certeza de que deseja cancelar essa tarefa pendente?", + "CancelPendingTask": "Tem certeza de que deseja cancelar esta tarefa pendente?", "Categories": "Categorias", "Category": "Categoria", "CertificateValidation": "Validação de certificado", - "CertificateValidationHelpText": "Alterar o quão estrita é a validação da certificação HTTPS", + "CertificateValidationHelpText": "Alterar a rigidez da validação da certificação HTTPS", "ChangeHasNotBeenSavedYet": "A alteração ainda não foi salva", "Clear": "Limpar", "ClearHistory": "Limpar histórico", @@ -93,7 +93,7 @@ "CouldNotConnectSignalR": "Não é possível conectar ao SignalR, a interface não atualizará", "Custom": "Personalizado", "CustomFilters": "Filtros personalizados", - "DatabaseMigration": "Migração de Banco de Dados", + "DatabaseMigration": "Migração de banco de dados", "Database": "Banco de dados", "Date": "Data", "Dates": "Datas", @@ -101,7 +101,7 @@ "DeleteAppProfile": "Excluir perfil do aplicativo", "DeleteApplication": "Excluir aplicativo", "DeleteApplicationMessageText": "Tem certeza de que deseja excluir o aplicativo '{name}'?", - "DeleteBackup": "Excluir Backup", + "DeleteBackup": "Excluir backup", "DeleteBackupMessageText": "Tem certeza de que deseja excluir o backup '{name}'?", "DeleteClientCategory": "Excluir Categoria de Cliente de Download", "DeleteDownloadClient": "Excluir cliente de download", @@ -110,8 +110,8 @@ "DeleteIndexerProxyMessageText": "Tem certeza de que deseja excluir o proxy do indexador '{name}'?", "DeleteNotification": "Excluir notificação", "DeleteNotificationMessageText": "Tem certeza de que deseja excluir a notificação '{name}'?", - "DeleteTag": "Excluir Etiqueta", - "DeleteTagMessageText": "Tem certeza de que deseja excluir a tag '{label}'?", + "DeleteTag": "Excluir etiqueta", + "DeleteTagMessageText": "Tem certeza de que deseja excluir a etiqueta '{label}'?", "Description": "Descrição", "Details": "Detalhes", "DevelopmentSettings": "Configurações de desenvolvimento", @@ -126,7 +126,7 @@ "DownloadClientStatusAllClientHealthCheckMessage": "Todos os clientes de download estão indisponíveis devido a falhas", "DownloadClientStatusSingleClientHealthCheckMessage": "Clientes de download indisponíveis devido a falhas: {downloadClientNames}", "DownloadClients": "Clientes de download", - "DownloadClientsSettingsSummary": "Configuração de clientes de download para integração com a pesquisa da interface do usuário do {appName}", + "DownloadClientsSettingsSummary": "Configuração de clientes de download para integração com a pesquisa da interface do {appName}", "Duration": "Duração", "Edit": "Editar", "EditIndexer": "Editar Indexador", @@ -139,7 +139,7 @@ "EnableInteractiveSearch": "Ativar pesquisa interativa", "EnableInteractiveSearchHelpText": "Será usado com a pesquisa interativa", "EnableRss": "Habilitar RSS", - "EnableRssHelpText": "Habilitar feed RSS para indexador", + "EnableRssHelpText": "Habilitar feed RSS para o indexador", "EnableSSL": "Habilitar SSL", "EnableSslHelpText": " Requer a reinicialização com a execução como administrador para fazer efeito", "Enabled": "Habilitado", @@ -163,7 +163,7 @@ "Fixed": "Corrigido", "FocusSearchBox": "Selecionar a caixa de pesquisa", "Folder": "Pasta", - "ForMoreInformationOnTheIndividualDownloadClients": "Para saber mais sobre cada cliente de download, clique nos botões de informações.", + "ForMoreInformationOnTheIndividualDownloadClients": "Para saber mais sobre os clientes de download individuais, clique nos botões de informações.", "FullSync": "Sincronização completa", "General": "Geral", "GeneralSettings": "Configurações gerais", @@ -173,8 +173,8 @@ "GrabTitle": "Obter Título", "Grabbed": "Obtido", "Grabs": "Obtenções", - "Health": "Saúde", - "HealthNoIssues": "Nenhum problema com sua configuração", + "Health": "Integridade", + "NoIssuesWithYourConfiguration": "Nenhum problema com sua configuração", "HideAdvanced": "Ocultar opções avançadas", "History": "Histórico", "HistoryCleanup": "Histórico de Limpeza", @@ -195,16 +195,16 @@ "IndexerDetails": "Detalhes do Indexador", "IndexerDisabled": "Indexador Desabilitado", "IndexerFailureRate": "Taxa de falha do indexador", - "IndexerFlags": "Sinalizadores do Indexador", + "IndexerFlags": "Sinalizadores do indexador", "IndexerHealthCheckNoIndexers": "Não há indexadores habilitados, o {appName} não retornará resultados para a pesquisa", "IndexerInfo": "Info do Indexador", "IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Todos os indexadores estão indisponíveis devido a falhas por mais de 6 horas", "IndexerLongTermStatusUnavailableHealthCheckMessage": "Indexadores indisponíveis devido a falhas por mais de 6 horas: {indexerNames}", "IndexerName": "Nome do Indexador", - "IndexerNoDefCheckMessage": "Os indexadores não têm definição e não funcionarão: {0}. Por favor, remova e (ou) adicione novamente ao {appName}", + "IndexerNoDefinitionCheckHealthCheckMessage": "Os indexadores não têm definição e não funcionarão: {indexerNames}. Remova e (ou) adicione novamente ao {appName}.", "IndexerObsoleteCheckMessage": "Os seguintes indexadores são obsoletos ou foram atualizados: {0}. Remova-os e/ou adicione-os novamente ao {appName}", "IndexerPriority": "Prioridade do indexador", - "IndexerPriorityHelpText": "Prioridade do Indexador de 1 (Mais Alta) a 50 (Mais Baixa). Padrão: 25.", + "IndexerPriorityHelpText": "Prioridade do indexador, de 1 (mais alta) a 50 (mais baixa). Padrão: 25.", "IndexerProxies": "Proxies do Indexador", "IndexerProxy": "Proxy do Indexador", "IndexerProxyStatusAllUnavailableHealthCheckMessage": "Todos os proxies estão indisponíveis devido a falhas", @@ -225,30 +225,30 @@ "InstanceNameHelpText": "Nome da instância na aba e para o nome do aplicativo Syslog", "InteractiveSearch": "Pesquisa interativa", "Interval": "Intervalo", - "KeyboardShortcuts": "Atalhos do Teclado", + "KeyboardShortcuts": "Atalhos do teclado", "Label": "Rótulo", "Language": "Idioma", - "LastDuration": "Última Duração", - "LastExecution": "Última Execução", + "LastDuration": "Última duração", + "LastExecution": "Última execução", "LastFailure": "Última Falha", - "LastWriteTime": "Hora da Última Gravação", - "LaunchBrowserHelpText": " Abrir o navegador Web e navegar até a página inicial do {appName} ao iniciar o aplicativo.", + "LastWriteTime": "Hora da última gravação", + "LaunchBrowserHelpText": " Abrir um navegador e navegar até a página inicial do {appName} ao iniciar o aplicativo.", "Level": "Nível", "Link": "Link", - "LogFiles": "Arquivos de registro", - "LogLevel": "Nível de registro", - "LogLevelTraceHelpTextWarning": "O registro em log deve ser habilitado apenas temporariamente", + "LogFiles": "Arquivos de log", + "LogLevel": "Nível de registro em log", + "LogLevelTraceHelpTextWarning": "O registro em log para rastreamento deve ser habilitado apenas temporariamente", "Logging": "Registro em log", "Logs": "Registros", - "MIA": "Desaparecidos", - "MaintenanceRelease": "Versão de manutenção: correções de bugs e outros aprimoramentos. Consulte o Histórico de Commit do Github para obter mais detalhes", + "MIA": "Ausentes", + "MaintenanceRelease": "Versão de manutenção: correções de bugs e outras melhorias. Consulte o Histórico de commit do Github para saber mais", "Manual": "Manual", "MappedCategories": "Categorias Mapeadas", "MappedDrivesRunningAsService": "As unidades de rede mapeadas não estão disponíveis quando executadas como um serviço do Windows. Consulte as Perguntas frequentes para saber mais", "MassEditor": "Editor em Massa", "Mechanism": "Mecanismo", "Message": "Mensagem", - "MinimumSeeders": "Mínimo de Seeders", + "MinimumSeeders": "Mínimo de semeadores", "MinimumSeedersHelpText": "Semeadores mínimos exigidos pelo aplicativo para o indexador baixar", "Mode": "Modo", "More": "Mais", @@ -414,7 +414,7 @@ "SyncProfile": "Perfil de Sincronização", "SyncProfiles": "Perfis de Sincronização", "System": "Sistema", - "SystemTimeCheckMessage": "A hora do sistema está desligada por mais de 1 dia. Tarefas agendadas podem não ser executadas corretamente até que o horário seja corrigido", + "SystemTimeHealthCheckMessage": "A hora do sistema está desligada por mais de 1 dia. Tarefas agendadas podem não ser executadas corretamente até que o horário seja corrigido", "TVSearchTypes": "Tipos de Pesquisa de Seriados", "TableOptions": "Opções de Tabela", "TableOptionsColumnsMessage": "Escolha quais colunas são visíveis e em que ordem aparecem", @@ -429,7 +429,7 @@ "TestAllApps": "Testar todos os aplicativos", "TestAllClients": "Testar todos os clientes", "TestAllIndexers": "Testar todos os indexadores", - "TheLatestVersionIsAlreadyInstalled": "A versão mais recente do {appName} já está instalada", + "OnLatestVersion": "A versão mais recente do {appName} já está instalada", "Theme": "Tema", "ThemeHelpText": "Alterar o tema da interface do usuário do aplicativo, o tema 'Auto' usará o tema do sistema operacional para definir o modo Claro ou Escuro. Inspirado por {inspiredBy}.", "Time": "Tempo", @@ -462,7 +462,7 @@ "UnableToAddANewNotificationPleaseTryAgain": "Não foi possível adicionar uma nova notificação. Tente novamente.", "UnableToLoadAppProfiles": "Não foi possível carregar os perfis de aplicativos", "ApplicationsLoadError": "Não é possível carregar a lista de aplicativos", - "UnableToLoadBackups": "Não foi possível carregar os backups", + "BackupsLoadError": "Não foi possível carregar os backups", "UnableToLoadDevelopmentSettings": "Não foi possível carregar as configurações de desenvolvimento", "DownloadClientsLoadError": "Não foi possível carregar os clientes de download", "UnableToLoadGeneralSettings": "Não foi possível carregar as configurações gerais", @@ -475,11 +475,11 @@ "UnsavedChanges": "Alterações Não Salvas", "UnselectAll": "Desmarcar tudo", "UpdateAutomaticallyHelpText": "Baixe e instale atualizações automaticamente. Você ainda poderá instalar a partir do Sistema: Atualizações", - "UpdateAvailableHealthCheckMessage": "Nova atualização está disponível", + "UpdateAvailableHealthCheckMessage": "Nova atualização está disponível: {version}", "UpdateStartupNotWritableHealthCheckMessage": "Não é possível instalar a atualização porque a pasta de inicialização '{startupFolder}' não pode ser gravada pelo usuário '{userName}'.", "UpdateStartupTranslocationHealthCheckMessage": "Não é possível instalar a atualização porque a pasta de inicialização '{startupFolder}' está em uma pasta de translocação de aplicativo.", "UpdateUiNotWritableHealthCheckMessage": "Não é possível instalar a atualização porque a pasta de IU '{uiFolder}' não pode ser gravada pelo usuário '{userName}'.", - "UpdateMechanismHelpText": "Use o atualizador integrado do {appName} ou um script", + "UpdateMechanismHelpText": "Usar o atualizador integrado do {appName} ou um script", "UpdateScriptPathHelpText": "Caminho para um script personalizado que usa um pacote de atualização extraído e lida com o restante do processo de atualização", "Updates": "Atualizações", "Uptime": "Tempo de atividade", @@ -499,21 +499,21 @@ "Yes": "Sim", "YesCancel": "Sim, Cancelar", "Yesterday": "Ontem", - "ApplyChanges": "Aplicar Mudanças", + "ApplyChanges": "Aplicar mudanças", "ApplyTagsHelpTextAdd": "Adicionar: adicione as etiquetas à lista existente de etiquetas", "Implementation": "Implementação", "SelectIndexers": "Pesquisar indexadores", "ApplyTagsHelpTextHowToApplyApplications": "Como aplicar tags ao autor selecionado", - "ApplyTagsHelpTextHowToApplyIndexers": "Como aplicar tags aos indexadores selecionados", + "ApplyTagsHelpTextHowToApplyIndexers": "Como aplicar etiquetas aos indexadores selecionados", "ApplyTagsHelpTextRemove": "Remover: remove as etiquetas inseridas", - "ApplyTagsHelpTextReplace": "Substituir: Substitua as etiquetas pelas etiquetas inseridas (não digite nenhuma etiqueta para limpar todas as etiquetas)", + "ApplyTagsHelpTextReplace": "Substituir: substitui as etiquetas atuais pelas inseridas (deixe em branco para limpar todas as etiquetas)", "CountDownloadClientsSelected": "{count} cliente(s) de download selecionado(s)", "CountIndexersSelected": "{count} indexador(es) selecionado(s)", "DeleteSelectedApplicationsMessageText": "Tem certeza de que deseja excluir {count} aplicativos selecionados?", "DeleteSelectedDownloadClients": "Excluir cliente(s) de download", - "DeleteSelectedDownloadClientsMessageText": "Tem certeza de que deseja excluir {count} cliente(s) de download selecionado(s)?", - "DeleteSelectedIndexersMessageText": "Tem certeza de que deseja excluir {count} indexadores selecionados?", - "DownloadClientPriorityHelpText": "Priorizar vários clientes de download. Usamos um rodízio para clientes com a mesma prioridade.", + "DeleteSelectedDownloadClientsMessageText": "Tem certeza de que deseja excluir o(s) {count} cliente(s) de download selecionado(s)?", + "DeleteSelectedIndexersMessageText": "Tem certeza de que deseja excluir o(s) {count} indexador(es) selecionado(s)?", + "DownloadClientPriorityHelpText": "Priorizar vários clientes de download. Usamos uma distribuição equilibrada para clientes com a mesma prioridade.", "EditSelectedDownloadClients": "Editar clientes de download selecionados", "EditSelectedIndexers": "Editar indexadores selecionados", "ManageDownloadClients": "Gerenciar clientes de download", @@ -531,17 +531,17 @@ "FoundCountReleases": "Encontrou {itemCount} lançamentos", "DeleteSelectedIndexer": "Excluir indexador selecionado", "IndexerCategories": "Categorias do indexador", - "IndexerDownloadClientHelpText": "Especifique qual cliente de download é usado para capturas feitas no {appName} a partir deste indexador", + "IndexerDownloadClientHelpText": "Especifique qual cliente de download é usado para baixar deste indexador no {appName}", "IndexerStatus": "Status do indexador", "ManageApplications": "Gerenciar aplicativos", "NoHistoryFound": "Nenhum histórico encontrado", "PackSeedTime": "Tempo de Semente do Pacote", "PackSeedTimeHelpText": "A hora em que um torrent de pacote (temporada ou discografia) deve ser propagado antes de parar, vazio é o padrão do aplicativo", "QueryType": "Tipo de consulta", - "SeedTime": "Tempo de Semeação", + "SeedTime": "Tempo de semeadura", "SearchAllIndexers": "Pesquisar todos os indexadores", "SearchCountIndexers": "Pesquisar {count} indexador(es)", - "SeedRatio": "Proporção de sementes", + "SeedRatio": "Proporção de semeadura", "SeedRatioHelpText": "A proporção que um torrent deve atingir antes de parar, vazio é o padrão do aplicativo", "SeedTimeHelpText": "O tempo que um torrent deve ser propagado antes de parar, vazio é o padrão do aplicativo", "SelectedCountOfCountReleases": "{selectedCount} de {itemCount} lançamentos selecionados", @@ -557,11 +557,11 @@ "IndexerDownloadClientHealthCheckMessage": "Indexadores com clientes de download inválidos: {indexerNames}.", "DeleteAppProfileMessageText": "Tem certeza de que deseja excluir o perfil do aplicativo '{name}'?", "AppUpdated": "{appName} atualizado", - "AppUpdatedVersion": "{appName} foi atualizado para a versão `{version}`, para obter as alterações mais recentes, você precisará recarregar {appName}", - "ConnectionLostToBackend": "{appName} perdeu a conexão com o backend e precisará ser recarregado para restaurar a funcionalidade.", + "AppUpdatedVersion": "O {appName} foi atualizado para a versão `{version}`. Para obter as alterações mais recentes, recarregue o {appName}", + "ConnectionLostToBackend": "O {appName} perdeu a conexão com o backend e precisará ser recarregado para restaurar a funcionalidade.", "RecentChanges": "Mudanças Recentes", "WhatsNew": "O que há de novo?", - "ConnectionLostReconnect": "{appName} tentará se conectar automaticamente ou você pode clicar em recarregar abaixo.", + "ConnectionLostReconnect": "O {appName} tentará se conectar automaticamente ou você pode clicar em Recarregar abaixo.", "AddApplicationImplementation": "Adicionar Aplicativo - {implementationName}", "AddConnectionImplementation": "Adicionar conexão - {implementationName}", "EditApplicationImplementation": "Editar Aplicativo - {implementationName}", @@ -601,17 +601,17 @@ "NoIndexerCategories": "Nenhuma categoria encontrada para este indexador", "AuthenticationRequiredPasswordConfirmationHelpTextWarning": "Confirme a nova senha", "PasswordConfirmation": "Confirmação Da Senha", - "InvalidUILanguage": "Sua UI está definida com um idioma inválido, corrija-a e salve suas configurações", - "DownloadClientQbittorrentSettingsContentLayoutHelpText": "Seja para usar o layout de conteúdo configurado do qBittorrent, o layout original do torrent ou sempre criar uma subpasta (qBittorrent 4.3.2+)", - "DownloadClientQbittorrentSettingsContentLayout": "Layout de Conteúdo", + "InvalidUILanguage": "Sua interface está definida com um idioma inválido, corrija-o e salve suas configurações", + "DownloadClientQbittorrentSettingsContentLayoutHelpText": "Se devemos usar o layout de conteúdo configurado do qBittorrent, o layout original do torrent ou sempre criar uma subpasta (qBittorrent 4.3.2+)", + "DownloadClientQbittorrentSettingsContentLayout": "Layout de conteúdo", "IndexerId": "ID do Indexador", "DownloadClientAriaSettingsDirectoryHelpText": "Local opcional para colocar downloads, deixe em branco para usar o local padrão do Aria2", - "ManageClients": "Gerenciar Clientes", + "ManageClients": "Gerenciar clientes", "NoApplicationsFound": "Nenhum aplicativo encontrado", "NotificationsEmailSettingsUseEncryptionHelpText": "Se preferir usar criptografia se configurado no servidor, usar sempre criptografia via SSL (somente porta 465) ou StartTLS (qualquer outra porta) ou nunca usar criptografia", "NotificationsEmailSettingsUseEncryption": "Usar Criptografia", "IndexerHDBitsSettingsPasskeyHelpText": "Chave de Acesso dos Detalhes do Usuário", - "IndexerSettingsPasskey": "Chave de Acesso", + "IndexerSettingsPasskey": "Chave de acesso", "IndexerAlphaRatioSettingsExcludeSceneHelpText": "Excluir lançamentos SCENE dos resultados", "IndexerAlphaRatioSettingsFreeleechOnlyHelpText": "Pesquisar lançamentos freeleech somente", "IndexerBeyondHDSettingsFreeleechOnlyHelpText": "Pesquisar lançamentos freeleech somente", @@ -632,7 +632,7 @@ "IndexerGazelleGamesSettingsSearchGroupNamesHelpText": "Pesquisar lançamentos por nomes de grupos", "IndexerHDBitsSettingsCodecs": "Codecs", "IndexerHDBitsSettingsMediumsHelpText": "se não for especificado, todas as opções serão usadas.", - "IndexerHDBitsSettingsOriginsHelpText": "se não for especificado, todas as opções serão usadas.", + "IndexerHDBitsSettingsOriginsHelpText": "Se não for especificado, todas as opções serão usadas.", "IndexerHDBitsSettingsUseFilenames": "Usar nomes de arquivos", "IndexerHDBitsSettingsUsernameHelpText": "Nome de Usuário do Site", "IndexerHDBitsSettingsMediums": "Meios", @@ -652,7 +652,7 @@ "IndexerSettingsApiUser": "Usuário da API", "IndexerSettingsBaseUrl": "URL Base", "IndexerSettingsCookie": "Cookie", - "IndexerSettingsCookieHelpText": "Cookie do Site", + "IndexerSettingsCookieHelpText": "Cookie do site", "IndexerSettingsFreeleechOnly": "Só Freeleech", "IndexerSettingsGrabLimit": "Limite de Captura", "IndexerSettingsGrabLimitHelpText": "O número máximo de capturas conforme especificado pela respectiva unidade que {appName} permitirá ao site", @@ -661,8 +661,8 @@ "IndexerSettingsPackSeedTime": "Tempo de Semeação de Pack", "IndexerSettingsQueryLimit": "Limite de Consultas", "IndexerSettingsRssKey": "Chave RSS", - "IndexerSettingsSeedRatio": "Proporção de Semeação", - "IndexerSettingsSeedTime": "Tempo de Semeação", + "IndexerSettingsSeedRatio": "Proporção de semeação", + "IndexerSettingsSeedTime": "Tempo de semeação", "IndexerSettingsSeedTimeHelpText": "O tempo que um torrent deve ser semeado antes de parar, vazio usa o padrão do cliente de download", "IndexerSettingsVipExpiration": "Expiração VIP", "IndexerTorrentSyndikatSettingsApiKeyHelpText": "Chave de API do site", @@ -683,54 +683,54 @@ "IndexerSettingsQueryLimitHelpText": "O número máximo de consultas especificadas pela respectiva unidade que {appName} permitirá ao site", "IndexerSettingsSeedRatioHelpText": "A proporção que um torrent deve atingir antes de parar, vazio usa o padrão do cliente de download. A proporção deve ser de pelo menos 1,0 e seguir as regras dos indexadores", "DefaultCategory": "Categoria Padrão", - "Destination": "Destinação", + "Destination": "Destino", "Directory": "Diretório", - "DownloadClientDelugeSettingsUrlBaseHelpText": "Adiciona um prefixo ao URL json do deluge, consulte {url}", - "DownloadClientFloodSettingsAdditionalTags": "Etiquetas Adicionais", + "DownloadClientDelugeSettingsUrlBaseHelpText": "Adiciona um prefixo ao URL do JSON do Deluge, consulte {url}", + "DownloadClientFloodSettingsAdditionalTags": "Etiquetas adicionais", "DownloadClientFloodSettingsAdditionalTagsHelpText": "Adiciona propriedades de mídia como etiquetas. As dicas são exemplos.", - "DownloadClientFloodSettingsUrlBaseHelpText": "Adiciona um prefixo à API Flood, como {url}", + "DownloadClientFloodSettingsUrlBaseHelpText": "Adiciona um prefixo à API do Flood, como {url}", "DownloadClientFreeboxSettingsApiUrl": "URL da API", - "DownloadClientFreeboxSettingsApiUrlHelpText": "Defina o URL base da API Freebox com a versão da API, por exemplo, '{url}', o padrão é '{defaultApiUrl}'", - "DownloadClientFreeboxSettingsAppId": "ID do App", - "DownloadClientFreeboxSettingsAppIdHelpText": "ID do aplicativo fornecido ao criar acesso à API Freebox (ou seja, 'app_id')", - "DownloadClientFreeboxSettingsAppToken": "Token do App", - "DownloadClientFreeboxSettingsPortHelpText": "Porta usada para acessar a interface do Freebox, o padrão é '{port}'", - "DownloadClientNzbgetSettingsAddPausedHelpText": "Esta opção requer pelo menos NzbGet versão 16.0", + "DownloadClientFreeboxSettingsApiUrlHelpText": "Defina o URL base da API do Freebox com a versão da API, por exemplo, \"{url}\", o padrão é \"{defaultApiUrl}\"", + "DownloadClientFreeboxSettingsAppId": "ID do aplicativo", + "DownloadClientFreeboxSettingsAppIdHelpText": "ID do aplicativo fornecida ao criar acesso à API do Freebox (ou seja, \"app_id\")", + "DownloadClientFreeboxSettingsAppToken": "Token do aplicativo", + "DownloadClientFreeboxSettingsPortHelpText": "Porta usada para acessar a interface do Freebox, o padrão é \"{port}\"", + "DownloadClientNzbgetSettingsAddPausedHelpText": "Esta opção requer pelo menos a versão 16.0 do NzbGet", "DownloadClientPneumaticSettingsStrmFolder": "Pasta Strm", "DownloadClientPneumaticSettingsStrmFolderHelpText": "Os arquivos .strm nesta pasta serão importados pelo drone", - "DownloadClientQbittorrentSettingsFirstAndLastFirst": "Primeiro e Último Primeiro", - "DownloadClientQbittorrentSettingsSequentialOrder": "Ordem Sequencial", - "DownloadClientQbittorrentSettingsSequentialOrderHelpText": "Baixe em ordem sequencial (qBittorrent 4.1.0+)", - "DownloadClientQbittorrentSettingsUseSslHelpText": "Use uma conexão segura. Consulte Opções - UI da Web - 'Usar HTTPS em vez de HTTP' em qBittorrent.", - "DownloadClientRTorrentSettingsAddStopped": "Adicionar Parado", - "DownloadClientRTorrentSettingsAddStoppedHelpText": "Habilitando, irá adicionar torrents e magnets ao rTorrent em um estado parado. Isso pode quebrar os arquivos magnéticos.", + "DownloadClientQbittorrentSettingsFirstAndLastFirst": "Priorizar o primeiro e o último", + "DownloadClientQbittorrentSettingsSequentialOrder": "Ordem sequencial", + "DownloadClientQbittorrentSettingsSequentialOrderHelpText": "Baixar em ordem sequencial (qBittorrent 4.1.0+)", + "DownloadClientQbittorrentSettingsUseSslHelpText": "Usar uma conexão segura. Consulte Opções -> Interface de Usuário da Web -> \"Usar HTTPS ao invés do HTTP\" no qBittorrent.", + "DownloadClientRTorrentSettingsAddStopped": "Adicionar parado", + "DownloadClientRTorrentSettingsAddStoppedHelpText": "Habilitar esta opção adicionará os torrents e links magnéticos ao rTorrent em um estado parado. Isso pode quebrar os arquivos magnéticos.", "DownloadClientRTorrentSettingsDirectoryHelpText": "Local opcional para colocar downloads, deixe em branco para usar o local padrão do rTorrent", - "DownloadClientRTorrentSettingsUrlPath": "Caminho da URL", - "DownloadClientSettingsAddPaused": "Adicionar Pausado", - "DownloadClientSettingsInitialState": "Estado Inicial", - "DownloadClientSettingsInitialStateHelpText": "Estado inicial dos torrents adicionados a {clientName}", + "DownloadClientRTorrentSettingsUrlPath": "Caminho do URL", + "DownloadClientSettingsAddPaused": "Adicionar pausado", + "DownloadClientSettingsInitialState": "Estado inicial", + "DownloadClientSettingsInitialStateHelpText": "Estado inicial dos torrents adicionados ao {clientName}", "DownloadClientSettingsPriorityItemHelpText": "Prioridade de uso ao pegar itens", - "DownloadClientSettingsUrlBaseHelpText": "Adiciona um prefixo ao URL {clientName}, como {url}", - "DownloadClientSettingsUseSslHelpText": "Use conexão segura ao conectar-se a {clientName}", - "DownloadClientTransmissionSettingsUrlBaseHelpText": "Adiciona um prefixo ao URL rpc {clientName}, por exemplo, {url}, o padrão é '{defaultUrl}'", + "DownloadClientSettingsUrlBaseHelpText": "Adiciona um prefixo ao URL do {clientName}, como {url}", + "DownloadClientSettingsUseSslHelpText": "Usar conexão segura ao conectar-se ao {clientName}", + "DownloadClientTransmissionSettingsUrlBaseHelpText": "Adiciona um prefixo ao URL de chamada remota do {clientName}, por exemplo, {url}. O padrão é \"{defaultUrl}\"", "TorrentBlackholeSaveMagnetFilesExtension": "Salvar Arquivos Magnet com Extensão", "TorrentBlackholeTorrentFolder": "Pasta do Torrent", "UsenetBlackholeNzbFolder": "Pasta Nzb", "XmlRpcPath": "Caminho RPC XML", - "BlackholeFolderHelpText": "Pasta na qual {appName} armazenará o arquivo {extension}", + "BlackholeFolderHelpText": "Pasta na qual o {appName} armazenará o arquivo {extension}", "DownloadClientDownloadStationSettingsDirectoryHelpText": "Pasta compartilhada opcional para colocar downloads, deixe em branco para usar o local padrão do Download Station", "DownloadClientFloodSettingsTagsHelpText": "Etiquetas iniciais de um download. Para ser reconhecido, um download deve ter todas as etiquetas iniciais. Isso evita conflitos com downloads não relacionados.", - "DownloadClientFreeboxSettingsAppTokenHelpText": "Token do aplicativo recuperado ao criar acesso à API Freebox (ou seja, 'app_token')", - "DownloadClientFreeboxSettingsHostHelpText": "Nome do host ou endereço IP do host do Freebox, o padrão é '{url}' (só funcionará se estiver na mesma rede)", + "DownloadClientFreeboxSettingsAppTokenHelpText": "Token do aplicativo recuperado ao criar acesso à API do Freebox (ou seja, \"app_token\")", + "DownloadClientFreeboxSettingsHostHelpText": "Nome ou endereço IP do host do Freebox, o padrão é \"{url}\" (só funcionará se estiver na mesma rede)", "DownloadClientPneumaticSettingsNzbFolder": "Pasta Nzb", "DownloadClientPneumaticSettingsNzbFolderHelpText": "Esta pasta precisará estar acessível no XBMC", - "DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText": "Baixe a primeira e a última peças primeiro (qBittorrent 4.1.0+)", - "DownloadClientQbittorrentSettingsInitialStateHelpText": "Estado inicial para torrents adicionados ao qBittorrent. Observe que os Torrents Forçados não obedecem às restrições de semeação", - "DownloadClientRTorrentSettingsUrlPathHelpText": "Caminho para o endpoint XMLRPC, consulte {url}. Geralmente é RPC2 ou [caminho para ruTorrent]{url2} ao usar o ruTorrent.", + "DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText": "Baixe a primeira e a última partes antes (qBittorrent 4.1.0+)", + "DownloadClientQbittorrentSettingsInitialStateHelpText": "Estado inicial para torrents adicionados ao qBittorrent. Observe que torrents forçados não obedecem às restrições de semeadura", + "DownloadClientRTorrentSettingsUrlPathHelpText": "Caminho para o ponto de extremidade do XMLRPC, consulte {url}. Geralmente é RPC2 ou [caminho para ruTorrent]{url2} ao usar o ruTorrent.", "DownloadClientSettingsDefaultCategorySubFolderHelpText": "Categoria de reserva padrão se não existir nenhuma categoria mapeada para um lançamento. Adicionar uma categoria específica para {appName} evita conflitos com downloads não relacionados que não sejam de {appName}. Usar uma categoria é opcional, mas altamente recomendado. Cria um subdiretório [categoria] no diretório de saída.", "DownloadClientSettingsDefaultCategoryHelpText": "Categoria de reserva padrão se não existir nenhuma categoria mapeada para um lançamento. Adicionar uma categoria específica para {appName} evita conflitos com downloads não relacionados que não sejam de {appName}. Usar uma categoria é opcional, mas altamente recomendado.", "DownloadClientSettingsDestinationHelpText": "Especifica manualmente o destino do download, deixe em branco para usar o padrão", - "DownloadClientTransmissionSettingsDirectoryHelpText": "Local opcional para colocar downloads, deixe em branco para usar o local de transmissão padrão", + "DownloadClientTransmissionSettingsDirectoryHelpText": "Local opcional para colocar os downloads, deixe em branco para usar o local padrão do Transmission", "SecretToken": "Token Secreto", "TorrentBlackholeSaveMagnetFiles": "Salvar Arquivos Magnets", "TorrentBlackholeSaveMagnetFilesExtensionHelpText": "Extensão a ser usada para links magnet, o padrão é '.magnet'", @@ -744,7 +744,7 @@ "Mixed": "Misturado", "Donate": "Doar", "ApplicationSettingsSyncRejectBlocklistedTorrentHashes": "Sincronizar Lista de Bloqueio de Hashes de Torrents Rejeitados Enquanto Baixando", - "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "se um torrent for bloqueado por hash, ele pode não ser rejeitado corretamente durante o RSS/Pesquisa de alguns indexadores. Ativar isso permitirá que ele seja rejeitado após o torrent ser capturado, mas antes de ser enviado ao cliente.", + "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "Se um torrent for bloqueado por hash, pode não ser rejeitado corretamente durante a Sincronização RSS/Pesquisa em alguns indexadores. Ativar isso permitirá que ele seja rejeitado após obter o torrent, mas antes de enviar ao cliente.", "ClickToChangeQueryOptions": "Clique para alterar as opções de consulta", "IndexerMTeamTpSettingsApiKeyHelpText": "Chave API do Site (Encontrada no Painel de Controle do Usuário => Segurança => Laboratório)", "IndexerMTeamTpSettingsFreeleechOnlyHelpText": "Pesquise apenas lançamentos freeleech", @@ -754,11 +754,58 @@ "ProxyValidationBadRequest": "Falha ao testar o proxy. Código de status: {statusCode}", "ProxyValidationUnableToConnect": "Não foi possível conectar-se ao proxy: {exceptionMessage}. Verifique o log em torno deste erro para obter detalhes", "Default": "Padrão", - "ManualGrab": "Baixar Manualmente", + "ManualGrab": "Baixar manualmente", "Open": "Abrir", "OverrideAndAddToDownloadClient": "Substituir e adicionar ao cliente de download", "OverrideGrabModalTitle": "Substituir e Baixar - {title}", "PrioritySettings": "Prioridade: {priority}", - "GrabRelease": "Baixar Lançamento", - "SelectDownloadClientModalTitle": "{modalTitle} - Selecionar Cliente de Download" + "GrabRelease": "Baixar lançamento", + "SelectDownloadClientModalTitle": "{modalTitle} - Selecionar Cliente de Download", + "Any": "Quaisquer", + "Script": "Script", + "BuiltIn": "Embutido", + "InfoUrl": "URL de informações", + "PublishedDate": "Data de Publicação", + "Redirected": "Redirecionar", + "AverageQueries": "Média de Consultas", + "AverageGrabs": "Média de Capturas", + "AllSearchResultsHiddenByFilter": "Todos os resultados da pesquisa são ocultados pelo filtro aplicado.", + "PackageVersionInfo": "{packageVersion} por {packageAuthor}", + "HealthMessagesInfoBox": "Para saber mais sobre a causa dessas mensagens de verificação de integridade, clique no link da wiki (ícone de livro) no final da linha ou verifique os [logs]({link}). Se tiver dificuldade em interpretar essas mensagens, entre em contato com nosso suporte nos links abaixo.", + "LogSizeLimit": "Limite de Tamanho do Registro", + "LogSizeLimitHelpText": "Tamanho máximo do arquivo de registro em MB antes do arquivamento. O padrão é 1 MB.", + "PreferMagnetUrlHelpText": "Quando ativado, este indexador preferirá o uso de URLs magnéticos para captura com substituto para links de torrent", + "IndexerSettingsPreferMagnetUrl": "Preferir URL Magnético", + "IndexerSettingsPreferMagnetUrlHelpText": "Quando ativado, este indexador preferirá o uso de URLs magnéticos para captura com substituto para links de torrent", + "PreferMagnetUrl": "Preferir URL Magnético", + "IndexerPassThePopcornSettingsGoldenPopcornOnly": "Apenas Golden Popcorn", + "IndexerPassThePopcornSettingsGoldenPopcornOnlyHelpText": "Pesquisar somente lançamentos em Golden Popcorn", + "IndexerAvistazSettingsFreeleechOnlyHelpText": "Pesquisar apenas lançamentos freeleech", + "IndexerAvistazSettingsUsernameHelpText": "Nome de Usuário do Site", + "IndexerAvistazSettingsPasswordHelpText": "Senha do Site", + "IndexerAvistazSettingsPidHelpText": "PID da página Minha Conta ou Meu Perfil", + "IndexerAvistazSettingsUsernameHelpTextWarning": "Somente membros com rank e acima podem usar a API neste indexador.", + "RestartReloadNote": "Observação: o {appName} reiniciará automaticamente e recarregará a interface durante o processo de restauração.", + "DockerUpdater": "Atualize o contêiner do Docker para receber a atualização", + "Download": "Baixar", + "ErrorRestoringBackup": "Erro ao restaurar o backup", + "ExternalUpdater": "O {appName} está configurado para usar um mecanismo de atualização externo", + "FailedToFetchUpdates": "Falha ao buscar atualizações", + "LogFilesLocation": "Os arquivos de log estão localizados em: {location}", + "Logout": "Sair", + "NoEventsFound": "Nenhum evento encontrado", + "TheLogLevelDefault": "O nível de registro é padronizado como 'Info' e pode ser alterado em [Configurações Gerais](/settings/general)", + "UpdateAppDirectlyLoadError": "Incapaz de atualizar o {appName} diretamente,", + "UpdaterLogFiles": "Arquivos de log do atualizador", + "WouldYouLikeToRestoreBackup": "Gostaria de restaurar o backup '{name}'?", + "AptUpdater": "Usar apt para instalar atualizações", + "Install": "Instalar", + "InstallLatest": "Instalar o mais recente", + "InstallMajorVersionUpdate": "Instalar Atualização", + "InstallMajorVersionUpdateMessage": "Esta atualização instalará uma nova versão principal e pode não ser compatível com o seu sistema. Tem certeza de que deseja instalar esta atualização?", + "InstallMajorVersionUpdateMessageLink": "Verifique [{domain}]({url}) para obter mais informações.", + "FailedToFetchSettings": "Falha ao obter configurações", + "CurrentlyInstalled": "Atualmente instalado", + "PreviouslyInstalled": "Instalado anteriormente", + "DownloadClientUTorrentProviderMessage": "O uTorrent tem um histórico de incluir criptomineradores, malware e anúncios, recomendamos que você escolha outro cliente de download." } diff --git a/src/NzbDrone.Core/Localization/Core/ro.json b/src/NzbDrone.Core/Localization/Core/ro.json index 34d259532..2385c5ddf 100644 --- a/src/NzbDrone.Core/Localization/Core/ro.json +++ b/src/NzbDrone.Core/Localization/Core/ro.json @@ -57,7 +57,7 @@ "KeyboardShortcuts": "Scurtături din tastatură", "Info": "Info", "IndexerStatusUnavailableHealthCheckMessage": "Indexatoare indisponibile datorită erorilor: {indexerNames}", - "HealthNoIssues": "Nicio problemă de configurare", + "NoIssuesWithYourConfiguration": "Nicio problemă de configurare", "Error": "Eroare", "ConnectionLost": "Conexiune Pierdută", "Component": "Componentă", @@ -90,7 +90,7 @@ "Tags": "Etichete", "TableOptionsColumnsMessage": "Alege ce coloane sunt vizibile și în ce ordine apar", "TableOptions": "Opțiuni tabel", - "SystemTimeCheckMessage": "Ora sistemului este diferită cu mai mult de 1 zi. Operațiunile programate s-ar putea să nu funcționeze corect până când ora nu este corectată", + "SystemTimeHealthCheckMessage": "Ora sistemului este diferită cu mai mult de 1 zi. Operațiunile programate s-ar putea să nu funcționeze corect până când ora nu este corectată", "System": "Sistem", "Style": "Stil", "Status": "Status", @@ -178,7 +178,7 @@ "TestAllClients": "Testați toți clienții", "Today": "Astăzi", "UnableToAddANewNotificationPleaseTryAgain": "Imposibil de adăugat o nouă notificare, încercați din nou.", - "UnableToLoadBackups": "Imposibil de încărcat copiile de rezervă", + "BackupsLoadError": "Imposibil de încărcat copiile de rezervă", "DownloadClientsLoadError": "Nu se pot încărca clienții de descărcare", "URLBase": "Baza URL", "UrlBaseHelpText": "Pentru suport proxy invers, implicit este gol", @@ -226,7 +226,7 @@ "BackupRetentionHelpText": "Copiile de rezervă automate mai vechi decât perioada de păstrare vor fi curățate automat", "BindAddress": "Adresa de legare", "ChangeHasNotBeenSavedYet": "Modificarea nu a fost încă salvată", - "CloneProfile": "Clonați profil", + "CloneProfile": "Clonează Profil", "NoLeaveIt": "Nu, lasă-l", "DatabaseMigration": "Migrarea BD", "DeleteBackupMessageText": "Sigur doriți să ștergeți copia de siguranță „{0}”?", @@ -329,7 +329,7 @@ "IndexerHealthCheckNoIndexers": "Niciun indexator nu este activat, {appName} nu va returna rezultate la căutare.", "IndexerProxy": "Proxy indexator", "IndexerVipExpiredHealthCheckMessage": "Beneficiile VIP pentru indexator au expirat: {indexerNames}", - "IndexerNoDefCheckMessage": "Indexatorii nu au definiție și nu vor funcționa: {0}. Vă rugăm să-i ștergeți și (sau) să-i adăugați din nou în {appName}", + "IndexerNoDefinitionCheckHealthCheckMessage": "Indexatorii nu au definiție și nu vor funcționa: {indexerNames}. Vă rugăm să-i ștergeți și (sau) să-i adăugați din nou în {appName}", "IndexerRss": "RSS indexator", "EnabledRedirected": "Activat, Redirecționat", "Ended": "Încheiat", @@ -374,7 +374,7 @@ "NextExecution": "Următoarea execuție", "Remove": "Elimina", "Replace": "A inlocui", - "TheLatestVersionIsAlreadyInstalled": "Cea mai recentă versiune a {appName} este deja instalată", + "OnLatestVersion": "Cea mai recentă versiune a {appName} este deja instalată", "AddApplication": "Adaugă", "AddCustomFilter": "Adaugă filtru personalizat", "Track": "Urmă", @@ -444,7 +444,7 @@ "DisabledForLocalAddresses": "Dezactivat pentru adresele locale", "None": "Nici unul", "ResetAPIKeyMessageText": "Sigur doriți să vă resetați cheia API?", - "AddApplicationImplementation": "Adăugați aplicație - {implementareName}", + "AddApplicationImplementation": "Adăugați aplicație - {implementationName}", "AddCategory": "Adăugați categorie", "AddConnection": "Adăugați conexiune", "AddConnectionImplementation": "Adăugați conexiune - {implementationName}", @@ -468,5 +468,36 @@ "IndexerHDBitsSettingsMediums": "Mediu", "IndexerSettingsVipExpiration": "Expirare VIP", "Directory": "Director", - "CustomFilter": "Filtru personalizat" + "CustomFilter": "Filtru personalizat", + "SelectDownloadClientModalTitle": "{modalTitle} - Selectați clientul de descărcare", + "ProxyValidationBadRequest": "Testul proxy a eșuat. StatusCode: {statusCode}", + "GrabRelease": "Grab Release", + "BuiltIn": "Incorporat", + "Script": "Script", + "InfoUrl": "URL informații", + "PublishedDate": "Data publicării", + "AllSearchResultsHiddenByFilter": "Toate rezultatele sunt ascunse de filtrul aplicat", + "UpdateAvailableHealthCheckMessage": "O nouă versiune este disponibilă: {version}", + "Download": "Descarca", + "ErrorRestoringBackup": "Eroare la restaurarea copiei de rezervă", + "UpdateAppDirectlyLoadError": "Imposibil de actualizat direct {appName},", + "DockerUpdater": "Actualizați containerul Docker pentru a primi actualizarea", + "ExternalUpdater": "{appName} este configurat pentru a utiliza un mecanism de actualizare extern", + "NoEventsFound": "Nu s-au găsit evenimente", + "RestartReloadNote": "Notă: {appName} va reporni și reîncărca automat interfața de utilizare în timpul procesului de restaurare.", + "AptUpdater": "Utilizați apt pentru a instala actualizarea", + "InstallLatest": "Instalați cel mai recent", + "Clone": "Clonează", + "Stats": "Status", + "CurrentlyInstalled": "În prezent instalat", + "Mixed": "Fix", + "Season": "Motiv", + "ActiveIndexers": "Indexatorii activi", + "Any": "Oricare", + "AdvancedSettingsShownClickToHide": "Setări avansate afișate, click pentru a le ascunde", + "AdvancedSettingsHiddenClickToShow": "Setări avansate ascunse, click pentru afișare", + "ApiKeyValidationHealthCheckMessage": "Te rugăm să actualizezi cheia API astfel încât să aibă cel puțin {length} caractere. Poți face acest lucru din setări sau din fișierul de configurare", + "AddToDownloadClient": "Adaugă versiunea în clientul de descărcare", + "AddSyncProfile": "Adaugă profil de sincronizare", + "AddedToDownloadClient": "Versiune adăugată în client" } diff --git a/src/NzbDrone.Core/Localization/Core/ru.json b/src/NzbDrone.Core/Localization/Core/ru.json index 2bb71edd8..f31829160 100644 --- a/src/NzbDrone.Core/Localization/Core/ru.json +++ b/src/NzbDrone.Core/Localization/Core/ru.json @@ -4,20 +4,20 @@ "ClientPriority": "Приоритет клиента", "Backups": "Резервные копии", "BackupRetentionHelpText": "Автоматические резервные копии старше указанного периода будут автоматически удалены", - "BackupNow": "Сделать резервную копию", + "BackupNow": "Создать резервную копию", "BackupIntervalHelpText": "Периодичность автоматического резервного копирования", - "Backup": "Резервная копия", + "Backup": "Резервное копирование", "AutomaticSearch": "Автоматический поиск", - "Authentication": "Аутентификация", - "ApplyTags": "Применить тэги", + "Authentication": "Авторизация", + "ApplyTags": "Применить теги", "Apply": "Применить", - "AppDataLocationHealthCheckMessage": "Обновление будет не возможно, во избежание удаления данных программы во время обновления", + "AppDataLocationHealthCheckMessage": "Обновление не позволит сохранить AppData при обновлении", "ApiKey": "API ключ", "Analytics": "Аналитика", - "AddIndexer": "Добавить индексер", + "AddIndexer": "Добавить индексатор", "Added": "Добавлено", "Actions": "Действия", - "About": "Об", + "About": "О программе", "DeleteBackupMessageText": "Вы уверены, что хотите удалить резервную копию '{name}'?", "DeleteBackup": "Удалить резервную копию", "Delete": "Удалить", @@ -25,46 +25,46 @@ "Refresh": "Обновить", "RefreshMovie": "Обновить фильм", "Rss": "RSS", - "SendAnonymousUsageData": "Отправить анонимные данные об использовании", + "SendAnonymousUsageData": "Отправка анонимных данных об использовании", "UnableToLoadHistory": "Не удалось загрузить историю", - "MoreInfo": "Ещё инфо", - "MovieIndexScrollTop": "Индекс фильма: промотать вверх", + "MoreInfo": "Больше информации", + "MovieIndexScrollTop": "Индекс фильма: Прокрутить вверх", "Size": "Размер", - "OpenBrowserOnStart": "Открывать браузер при запуске", + "OpenBrowserOnStart": "Открыть браузер при запуске", "OpenThisModal": "Открыть это модальное окно", - "Options": "Опции", + "Options": "Параметры", "PackageVersion": "Версия пакета", "PageSize": "Размер страницы", - "PageSizeHelpText": "Количество показываемое на каждой страницы", + "PageSizeHelpText": "Количество элементов, отображаемых на каждой странице", "Password": "Пароль", "Peers": "Пиры", - "PendingChangesDiscardChanges": "Не применять изменения и выйти", - "PendingChangesStayReview": "Оставайтесь и просмотрите изменения", + "PendingChangesDiscardChanges": "Отменить изменения и выйти", + "PendingChangesStayReview": "Остаться и просмотреть изменения", "Port": "Порт", "PortNumber": "Номер порта", "LogFiles": "Файлы журнала", "Details": "Подробности", - "DownloadClients": "Клиенты для скачивания", + "DownloadClients": "Клиенты загрузки", "Filename": "Имя файла", "Files": "Файлы", "Filter": "Фильтр", "IndexerStatusAllUnavailableHealthCheckMessage": "Все индексаторы недоступны из-за ошибок", - "LogLevel": "Уровень журнала", - "LogLevelTraceHelpTextWarning": "Отслеживание журнала желательно включать только на короткое время", + "LogLevel": "Уровень журналирования", + "LogLevelTraceHelpTextWarning": "Включение трассировки журнала должно быть временным", "Logs": "Журналы", "Manual": "Ручной", "Mechanism": "Механизм", "NoBackupsAreAvailable": "Нет резервных копий", "DeleteDownloadClientMessageText": "Вы уверены, что хотите удалить клиент загрузки '{name}'?", - "Edit": "Редактирование", + "Edit": "Редактировать", "Enable": "Включить", "Indexer": "Индексатор", "SettingsShortDateFormat": "Короткий формат даты", "SettingsShowRelativeDates": "Показать относительные даты", - "SettingsShowRelativeDatesHelpText": "Показывать относительные (сегодня / вчера / и т. д.) или абсолютные даты", + "SettingsShowRelativeDatesHelpText": "Показывать относительные (Сегодня/Вчера/и т.п.) или абсолютные даты", "Docker": "Docker", "FocusSearchBox": "Поле поиска в фокусе", - "Reset": "Сбросить", + "Reset": "Сброс", "Add": "Добавить", "EnableSSL": "Включить SSL", "IndexerFlags": "Флаги индексатора", @@ -73,36 +73,36 @@ "Usenet": "Usenet", "AcceptConfirmationModal": "Окно подтверждения", "Today": "Сегодня", - "DeleteIndexerProxyMessageText": "Вы уверены, что хотите удалить тэг '{0}'?", - "ExistingTag": "Существующий тэг", + "DeleteIndexerProxyMessageText": "Вы уверены, что хотите удалить прокси индексатора '{name}'?", + "ExistingTag": "Существующий тег", "Failed": "Неудачно", - "IndexerProxyStatusUnavailableHealthCheckMessage": "Индексаторы недоступны из-за ошибок: {indexerProxyNames}", + "IndexerProxyStatusUnavailableHealthCheckMessage": "Прокси индексатора недоступны из-за ошибок: {indexerProxyNames}", "Indexers": "Индексаторы", "InteractiveSearch": "Интерактивный поиск", "Interval": "Интервал", "KeyboardShortcuts": "Горячие клавиши", "Language": "Язык", "LastWriteTime": "Последнее время записи", - "LaunchBrowserHelpText": " Открывать браузер и переходить на страницу {appName} при запуске программы.", + "LaunchBrowserHelpText": " Открыть браузер и перейти на страницу {appName} при запуске приложения.", "Level": "Уровень", - "Ok": "Ok", - "AddDownloadClient": "Добавить программу для скачивания", - "UpdateMechanismHelpText": "Используйте встроенную в {appName} функцию обновления или скрипт", + "Ok": "Ок", + "AddDownloadClient": "Добавить клиент загрузки", + "UpdateMechanismHelpText": "Использовать встроенный инструмент обновления {appName} или скрипт", "IndexerStatusUnavailableHealthCheckMessage": "Индексаторы недоступны из-за ошибок: {indexerNames}", - "NoTagsHaveBeenAddedYet": "Теги еще не добавлены", - "UnableToLoadTags": "Невозможно загрузить теги", - "AnalyticsEnabledHelpText": "Отправлять в {appName} анонимную информацию об использовании и ошибках. Анонимная статистика включает в себя информацию о браузере, какие страницы веб-интерфейса {appName} загружены, сообщения об ошибках, а также операционной системе. Мы используем эту информацию для выявления ошибок, а также для разработки нового функционала.", - "AuthenticationMethodHelpText": "Необходим логин и пароль для доступа в {appName}", + "NoTagsHaveBeenAddedYet": "Теги ещё не добавлены", + "UnableToLoadTags": "Не удалось загрузить теги", + "AnalyticsEnabledHelpText": "Отправлять анонимную информацию об использовании и ошибках на серверы {appName}. Анонимная статистика включает в себя информацию о вашем браузере, какие страницы веб-интерфейса {appName} вы используете, отчёты об ошибках, а также версию операционной системы и среды выполнения. Мы будем использовать эту информацию для разработки нового функционала и исправления ошибок.", + "AuthenticationMethodHelpText": "Необходимо ввести имя пользователя и пароль для доступа к {appName}", "BackupFolderHelpText": "Относительные пути будут в каталоге AppData {appName}", "BeforeUpdate": "До обновления", "BindAddress": "Привязать адрес", "BindAddressHelpText": "Действительный IP-адрес, локальный адрес или '*' для всех интерфейсов", - "Branch": "Ветка", + "Branch": "Ветвь", "BranchUpdate": "Ветвь для обновления {appName}", "BranchUpdateMechanism": "Ветвь, используемая внешним механизмом обновления", "BypassProxyForLocalAddresses": "Обход прокси для локальных адресов", - "Cancel": "Отменить", - "CancelPendingTask": "Вы уверены, что хотите убрать данную задачу из очереди?", + "Cancel": "Отмена", + "CancelPendingTask": "Вы уверены, что хотите отменить эту ожидающую задачу?", "CertificateValidation": "Проверка сертификата", "CertificateValidationHelpText": "Изменить строгое подтверждение сертификации НТТР", "ChangeHasNotBeenSavedYet": "Изменения ещё не вступили в силу", @@ -113,79 +113,79 @@ "ConnectionLost": "Соединение прервано", "Connections": "Соединения", "ConnectSettings": "Настройки соединения", - "CouldNotConnectSignalR": "Не возможно подключиться к SignalR, интерфейс не будет обновляться", + "CouldNotConnectSignalR": "Не удалось подключиться к SignalR, интерфейс не будет обновляться", "Custom": "Настраиваемый", "CustomFilters": "Настраиваемые фильтры", "Date": "Дата", "Dates": "Даты", - "DatabaseMigration": "Перенос БД", + "DatabaseMigration": "Миграция базы данных", "DeleteNotification": "Удалить уведомление", "DeleteNotificationMessageText": "Вы уверены, что хотите удалить уведомление '{name}'?", - "DeleteTag": "Удалить тэг", - "DeleteTagMessageText": "Вы уверены, что хотите удалить тэг '{label}'?", + "DeleteTag": "Удалить тег", + "DeleteTagMessageText": "Вы уверены, что хотите удалить тег '{label}'?", "Disabled": "Выключено", "Discord": "Discord", "Donations": "Пожертвования", - "DownloadClient": "Загрузчик", - "DownloadClientSettings": "Настройки клиента скачиваний", - "DownloadClientStatusAllClientHealthCheckMessage": "Все клиенты для скачивания недоступны из-за ошибок", - "DownloadClientStatusSingleClientHealthCheckMessage": "Клиенты для скачивания недоступны из-за ошибок: {downloadClientNames}", + "DownloadClient": "Клиент загрузки", + "DownloadClientSettings": "Настройки клиента загрузки", + "DownloadClientStatusAllClientHealthCheckMessage": "Все клиенты загрузок недоступны из-за ошибок", + "DownloadClientStatusSingleClientHealthCheckMessage": "Клиенты загрузок недоступны из-за ошибок: {downloadClientNames}", "EnableAutomaticSearch": "Включить автоматический поиск", - "EnableAutomaticSearchHelpText": "Будет использовано для автоматических поисков через интерфейс или {appName}", + "EnableAutomaticSearchHelpText": "Будет использовано при автоматическом поиске через интерфейс или {appName}", "Enabled": "Включено", "EnableInteractiveSearch": "Включить интерактивный поиск", "EnableRss": "Включить RSS", "Fixed": "Исправлено", - "Folder": "Папка", - "ForMoreInformationOnTheIndividualDownloadClients": "Для дополнительной информации пл программам скачивания нажмите эту кнопку.", - "General": "Основное", - "GeneralSettings": "Основные настройки", - "GeneralSettingsSummary": "Порт, SSL, логин/пароль, прокси, аналитика и обновления", + "Folder": "Каталог", + "ForMoreInformationOnTheIndividualDownloadClients": "Для получения дополнительной информации о каждом клиенте загрузки нажмите на кнопки информации.", + "General": "Общие", + "GeneralSettings": "Общие настройки", + "GeneralSettingsSummary": "Порт, SSL, имя пользователя/пароль, прокси-сервер, аналитика и обновления", "Grabbed": "Захвачено", - "Grabs": "Захватить", - "Health": "Здоровье", - "HealthNoIssues": "С вашей конфигурацией нет проблем", + "Grabs": "Захваты", + "Health": "Состояние", + "NoIssuesWithYourConfiguration": "С вашей конфигурацией нет проблем", "HideAdvanced": "Скрыть расширенные", "History": "История", "HomePage": "Домашняя страница", "Host": "Хост", "Hostname": "Имя хоста", - "IgnoredAddresses": "Проигнорированные адреса", + "IgnoredAddresses": "Игнорируемые адреса", "IllRestartLater": "Перезапущу позднее", - "IncludeHealthWarningsHelpText": "Включая предупреждения о здоровье", - "IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Все индексаторы недоступны из-за ошибок за последние 6 часов", - "IndexerLongTermStatusUnavailableHealthCheckMessage": "Все индексаторы недоступны из-за ошибок за последние 6 часов: {indexerNames}", - "IndexerPriority": "Приоритет индексаторов", - "IndexerPriorityHelpText": "Приоритет индексаторов от 1 (наивысший) до 50 (низший). По-умолчанию: 25.", - "IndexerProxyStatusAllUnavailableHealthCheckMessage": "Все индексаторы недоступны из-за ошибок", + "IncludeHealthWarningsHelpText": "Включая предупреждения о состоянии", + "IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Все индексаторы недоступны из-за ошибок более 6 часов", + "IndexerLongTermStatusUnavailableHealthCheckMessage": "Все индексаторы недоступны из-за ошибок более 6 часов: {indexerNames}", + "IndexerPriority": "Приоритет индексатора", + "IndexerPriorityHelpText": "Приоритет индексатора от 1 (Высший) до 50 (Низший). По умолчанию 25.", + "IndexerProxyStatusAllUnavailableHealthCheckMessage": "Все прокси индексатора недоступны из-за ошибок", "Info": "Информация", "Logging": "Журналирование", "Message": "Сообщение", "MIA": "MIA", "Mode": "Режим", - "MovieIndexScrollBottom": "Индекс фильма: промотать в низ", + "MovieIndexScrollBottom": "Индекс фильма: Прокрутить вниз", "Name": "Имя", "New": "Новый", "NoChanges": "Нет изменений", "NoLeaveIt": "Нет, оставить", - "NoLogFiles": "Нет файлов журнала", - "NoUpdatesAreAvailable": "Нет обновлений", - "OAuthPopupMessage": "Ваш браузер блокирует всплывающие окна", - "OnHealthIssueHelpText": "По вопросам здоровья", + "NoLogFiles": "Файлов журнала нет", + "NoUpdatesAreAvailable": "Обновления отсутствуют", + "OAuthPopupMessage": "Всплывающие окна блокируются вашим браузером", + "OnHealthIssueHelpText": "При проблемах с состоянием", "PendingChangesMessage": "У вас есть несохраненные изменения. Вы уверены, что хотите покинуть эту страницу?", - "Presets": "Предустановки", + "Presets": "Пресеты", "Protocol": "Протокол", "Proxy": "Прокси", - "ProxyBypassFilterHelpText": "Используйте ',' в качестве разделителя и '*.' как подстановочный знак для поддоменов", - "ProxyBadRequestHealthCheckMessage": "Не удалось проверить прокси. Код: {statusCode}", - "ProxyFailedToTestHealthCheckMessage": "Не удалось проверить прокси: {url}", - "ProxyResolveIpHealthCheckMessage": "Не удалось преобразовать IP-адрес для настроенного прокси-хоста {proxyHostName}", - "ProxyPasswordHelpText": "Нужно ввести имя пользователя и пароль только если они необходимы. В противном случае оставьте их пустыми.", + "ProxyBypassFilterHelpText": "Используйте ',' как разделитель и '*.' как подстановочный знак для поддоменов", + "ProxyBadRequestHealthCheckMessage": "Не удалось протестировать прокси. Код состояния: {statusCode}", + "ProxyFailedToTestHealthCheckMessage": "Не удалось протестировать прокси: {url}", + "ProxyResolveIpHealthCheckMessage": "Не удалось определить IP-адрес настроенного прокси-хоста {proxyHostName}", + "ProxyPasswordHelpText": "Вы должны указать имя пользователя и пароль только если они необходимы. В противном случае оставьте эти поля пустыми.", "ProxyType": "Тип прокси", - "ProxyUsernameHelpText": "Нужно ввести имя пользователя и пароль только если они необходимы. В противном случае оставьте их пустыми.", + "ProxyUsernameHelpText": "Вы должны указать имя пользователя и пароль только если они необходимы. В противном случае оставьте эти поля пустыми.", "Queue": "Очередь", "ReadTheWikiForMoreInformation": "Прочтите Wiki для получения дополнительной информации", - "ReleaseBranchCheckOfficialBranchMessage": "Ветка {0} не является допустимой веткой выпуска {appName}, вы не будете получать обновления", + "ReleaseBranchCheckOfficialBranchMessage": "Ветвь {0} не является действительной релизной ветвью {appName}, вы не будете получать обновления", "ReleaseStatus": "Статус релиза", "Reload": "Перезагрузить", "RemovedFromTaskQueue": "Удалено из очереди задач", @@ -194,9 +194,9 @@ "ResetAPIKey": "Сбросить API ключ", "Restart": "Перезапустить", "RestartNow": "Перезапустить сейчас", - "RestartRequiredHelpTextWarning": "Для вступления в силу требуется перезапуск", + "RestartRequiredHelpTextWarning": "Для применения изменений, требуется перезапуск", "Restore": "Восстановить", - "RestoreBackup": "Восстановить из резервной копии", + "RestoreBackup": "Восстановить резервную копию", "Result": "Результат", "Retention": "Удержание", "RssIsNotSupportedWithThisIndexer": "RSS не поддерживается этим индексатором", @@ -207,225 +207,605 @@ "ScriptPath": "Путь к скрипту", "Search": "Поиск", "Security": "Безопасность", - "Seeders": "Сиды", + "Seeders": "Сидеры", "SelectAll": "Выбрать все", "SetTags": "Установить теги", "Settings": "Настройки", - "SettingsEnableColorImpairedMode": "Версия для слабовидящих", - "SettingsEnableColorImpairedModeHelpText": "Стиль изменён чтобы слабовидящие лучше различали цвета", - "SettingsLongDateFormat": "Длинный формат даты", + "SettingsEnableColorImpairedMode": "Включить режим для людей с ограниченным цветовым восприятием", + "SettingsEnableColorImpairedModeHelpText": "Изменённый стиль позволяет пользователям с нарушениями цветового восприятия лучше различать цветовую информацию", + "SettingsLongDateFormat": "Расширенный формат даты", "SettingsTimeFormat": "Формат времени", "ShowAdvanced": "Показать расширенные", "ShowSearch": "Показать поиск", - "ShowSearchHelpText": "Показать копку поиска по наведению", + "ShowSearchHelpText": "Показать копку поиска при наведении", "Shutdown": "Выключить", "Sort": "Сортировка", - "Source": "Источник", - "SSLCertPassword": "Пароль SSL сертификата", - "SSLCertPasswordHelpText": "Пароль pfx файла", - "SSLCertPath": "Путь SSL сертификата", - "SSLCertPathHelpText": "Путь к pfx файлу", + "Source": "Исходный код", + "SSLCertPassword": "Пароль сертификата SSL", + "SSLCertPasswordHelpText": "Пароль файла pfx", + "SSLCertPath": "Путь к сертификату SSL", + "SSLCertPathHelpText": "Путь к файлу pfx", "StartTypingOrSelectAPathBelow": "Начните вводить или выберите путь ниже", - "StartupDirectory": "Каталог автозагрузки", + "StartupDirectory": "Каталог автозапуска", "Status": "Статус", "Style": "Стиль", "SuggestTranslationChange": "Предложить изменение перевода", - "SystemTimeCheckMessage": "Расхождение системного времени более чем на 1 день. Запланированные задачи могут работать некорректно, пока не будет исправлено время", - "TableOptionsColumnsMessage": "Выберите, какие столбцы отображаются и в каком порядке", - "TagIsNotUsedAndCanBeDeleted": "Тег не используется и может быть удален", - "TagsHelpText": "Применимо к фильмам с хотя бы одним подходящим тегом", + "SystemTimeHealthCheckMessage": "Системное время отклонилось более чем на 1 день. Запланированные задания могут не работать правильно, пока время не будет исправлено", + "TableOptionsColumnsMessage": "Выберите видимые столбцы и порядок их отображения", + "TagIsNotUsedAndCanBeDeleted": "Тег не используется и может быть удалён", + "TagsHelpText": "Применяется к индексаторам, имеющим хотя бы один совпадающий тег", "TagsSettingsSummary": "Посмотрите все теги и способы их использования. Неиспользуемые теги можно удалить", "Tasks": "Задачи", "Test": "Тест", "TestAll": "Тестировать все", - "TestAllClients": "Тестировать всех клиентов", + "TestAllClients": "Тестировать все клиенты", "Time": "Время", "Title": "Название", "Tomorrow": "Завтра", - "Torrent": "Торренты", + "Torrent": "Торрент", "Torrents": "Торренты", "Type": "Тип", "UI": "Пользовательский интерфейс", "UILanguage": "Язык пользовательского интерфейса", "UILanguageHelpTextWarning": "Требуется перезагрузка браузера", "UISettings": "Настройки пользовательского интерфейса", - "UnableToAddANewApplicationPleaseTryAgain": "Невозможно добавить новое уведомление, попробуйте еще раз.", - "UnableToAddANewAppProfilePleaseTryAgain": "Не удалось добавить новый профиль качества. Повторите попытку.", - "UnableToAddANewDownloadClientPleaseTryAgain": "Не удалось добавить новый клиент загрузки, попробуйте еще раз.", - "UnableToAddANewIndexerPleaseTryAgain": "Не удалось добавить новый индексатор, повторите попытку.", - "UnableToAddANewIndexerProxyPleaseTryAgain": "Не удалось добавить новый индексатор, повторите попытку.", - "UnableToAddANewNotificationPleaseTryAgain": "Невозможно добавить новое уведомление, попробуйте еще раз.", - "UnableToLoadBackups": "Невозможно загрузить резервные копии", - "DownloadClientsLoadError": "Невозможно загрузить загрузчики", - "UnableToLoadGeneralSettings": "Невозможно загрузить общие настройки", - "UnableToLoadNotifications": "Невозможно загрузить уведомления", - "UnableToLoadUISettings": "Не удалось загрузить настройки пользовательского интерфейса", + "UnableToAddANewApplicationPleaseTryAgain": "Не удалось добавить новое приложение, попробуйте ещё раз.", + "UnableToAddANewAppProfilePleaseTryAgain": "Не удалось добавить новый профиль приложения, попробуйте ещё раз.", + "UnableToAddANewDownloadClientPleaseTryAgain": "Не удалось добавить новый клиент загрузки, попробуйте ещё раз.", + "UnableToAddANewIndexerPleaseTryAgain": "Не удалось добавить новый индексатор, попробуйте ещё раз.", + "UnableToAddANewIndexerProxyPleaseTryAgain": "Не удалось добавить новый прокси индексатора, попробуйте ещё раз.", + "UnableToAddANewNotificationPleaseTryAgain": "Не удалось добавить новое уведомление, попробуйте ещё раз.", + "BackupsLoadError": "Не удалось загрузить резервные копии", + "DownloadClientsLoadError": "Не удалось загрузить клиенты загрузки", + "UnableToLoadGeneralSettings": "Не удалось загрузить общие настройки", + "UnableToLoadNotifications": "Не удалось загрузить уведомления", + "UnableToLoadUISettings": "Не удалось загрузить настройки интерфейса", "UnsavedChanges": "Несохраненные изменения", - "UnselectAll": "Снять все выделения", - "UpdateAutomaticallyHelpText": "Автоматически загружать и устанавливать обновления. Вы так же можете установить в Система: Обновления", - "UpdateStartupNotWritableHealthCheckMessage": "Невозможно установить обновление так как загрузочная папка '{startupFolder}' недоступна для записи для пользователя '{userName}'.", - "UpdateStartupTranslocationHealthCheckMessage": "Не удается установить обновление, поскольку папка автозагрузки \"{startupFolder}\" находится в папке перемещения приложений.", - "UpdateUiNotWritableHealthCheckMessage": "Невозможно установить обновление так как UI папка '{uiFolder}' недоступна для записи для пользователя '{userName}'.", - "UpdateScriptPathHelpText": "Путь к пользовательскому скрипту, который обрабатывает остатки после процесса обновления", + "UnselectAll": "Снять выделение со всех", + "UpdateAutomaticallyHelpText": "Автоматически загружать и устанавливать обновления Вы по-прежнему сможете выполнить установку из раздела Система: Обновления", + "UpdateStartupNotWritableHealthCheckMessage": "Невозможно установить обновление, так как каталог автозагрузки '{startupFolder}' недоступен для записи для пользователя '{userName}'.", + "UpdateStartupTranslocationHealthCheckMessage": "Невозможно установить обновление, так как каталог автозагрузки '{startupFolder}' находится в каталоге перемещения приложений.", + "UpdateUiNotWritableHealthCheckMessage": "Невозможно установить обновление, так как каталог интерфейса '{uiFolder}' недоступен для записи для пользователя '{userName}'.", + "UpdateScriptPathHelpText": "Путь к пользовательскому скрипту, который извлекает пакет обновления и обрабатывает оставшуюся часть процесса обновления", "Uptime": "Время работы", - "URLBase": "Базовый URL", - "UrlBaseHelpText": "Для поддержки обратного прокси, по умолчанию пусто", + "URLBase": "Базовый URL-адрес", + "UrlBaseHelpText": "Для поддержки обратного прокси, значение по умолчанию - пустая строка", "UseProxy": "Использовать прокси", - "Username": "Пользователь", + "Username": "Имя пользователя", "Version": "Версия", "View": "Просмотр", - "AddingTag": "Добавить ярлык", - "SSLPort": "SSL порт", - "UILanguageHelpText": "Язык, который {appName} будет использовать для пользовательского интерфейса", - "EnableSslHelpText": " Требуется перезапуск от администратора", - "ErrorLoadingContents": "Ошибка при загрузке содержимого", + "AddingTag": "Добавление тега", + "SSLPort": "Порт SSL", + "UILanguageHelpText": "Язык, используемый {appName} для интерфейса пользователя", + "EnableSslHelpText": " Для применения изменений требуется перезапуск с правами администратора", + "ErrorLoadingContents": "Ошибка при загрузке контента", "Events": "События", "EventType": "Тип события", "Exception": "Исключение", - "FeatureRequests": "Будущие запросы", - "Warn": "Предупреждать", + "FeatureRequests": "Запросы функций", + "Warn": "Предупреждение", "Wiki": "Wiki", - "YesCancel": "Да, отменить", + "YesCancel": "Да, Отмена", "Yesterday": "Вчера", "NotificationTriggers": "Триггеры уведомления", - "ApplicationStatusCheckAllClientMessage": "Все листы недоступны из-за ошибок", + "ApplicationStatusCheckAllClientMessage": "Все приложения недоступны из-за ошибок", "Automatic": "Автоматически", - "DeleteApplicationMessageText": "Вы уверены, что хотите удалить уведомление '{0}'?", - "DeleteDownloadClient": "Удалить программу для скачивания", - "EnableInteractiveSearchHelpText": "Будет использовано при автоматических поисках", + "DeleteApplicationMessageText": "Вы уверены, что хотите удалить приложение '{name}'?", + "DeleteDownloadClient": "Удалить клиент загрузки", + "EnableInteractiveSearchHelpText": "Будет использовано при интерактивном поиске", "Error": "Ошибка", "NoChange": "Нет изменений", "Age": "Возраст", "All": "Все", - "AllIndexersHiddenDueToFilter": "Все фильмы спрятаны в соответствии с фильтром.", - "AppDataDirectory": "Директория AppData", + "AllIndexersHiddenDueToFilter": "Все индексаторы скрыты применённым фильтром.", + "AppDataDirectory": "Каталог AppData", "Reddit": "Reddit", "System": "Система", - "TableOptions": "Опции таблицы", - "TagCannotBeDeletedWhileInUse": "Невозможно удалить во время использования", - "Tags": "Тэги", - "ApplicationStatusCheckSingleClientMessage": "Листы недоступны из-за ошибок: {0}", + "TableOptions": "Параметры таблицы", + "TagCannotBeDeletedWhileInUse": "Удаление невозможно во время использования", + "Tags": "Теги", + "ApplicationStatusCheckSingleClientMessage": "Приложения недоступны из-за ошибок: {0}", "EditIndexer": "Редактировать индексатор", - "MaintenanceRelease": "Техническая версия: исправления ошибок и другие улучшения. См. Историю коммитов Github для более подробной информации", + "MaintenanceRelease": "Технический релиз: исправление ошибок и другие улучшения. Подробнее см. в истории коммитов Github.", "Filters": "Фильтры", "HistoryCleanupDaysHelpText": "Установите 0, чтобы отключить автоматическую очистку", - "HistoryCleanupDaysHelpTextWarning": "Файлы в корзине старше указанного количества дней будут очищены автоматически", - "OnApplicationUpdateHelpText": "О обновлении приложения", - "OnGrab": "При захвате", - "OnHealthIssue": "О проблемах в системе", - "OnApplicationUpdate": "О обновлении приложения", + "HistoryCleanupDaysHelpTextWarning": "Элементы истории, старше указанного количества дней, будут автоматически удалены", + "OnApplicationUpdateHelpText": "При обновлении приложения", + "OnGrab": "При захвате релиза", + "OnHealthIssue": "При проблемах с состоянием", + "OnApplicationUpdate": "При обновлении приложения", "TestAllIndexers": "Тестировать все индексаторы", - "UserAgentProvidedByTheAppThatCalledTheAPI": "User-Agent, представленный приложением, который вызывает API", - "NotificationTriggersHelpText": "Выберите, какие события должны вызвать это уведомление", + "UserAgentProvidedByTheAppThatCalledTheAPI": "User-Agent, предоставленный приложением, вызвавшим API", + "NotificationTriggersHelpText": "Выберите события, которые должны вызвать это уведомление", "NetCore": ".NET", - "GrabReleases": "Захватить релиз", + "GrabReleases": "Захватить релиз(ы)", "UnableToLoadIndexers": "Не удалось загрузить индексаторы", - "Link": "Ссылки", - "MappedDrivesRunningAsService": "Подключённые сетевые диски недоступны, если программа запущена как сервис. Обратитесь в FAQ за дополнительной информацией", + "Link": "Ссылка", + "MappedDrivesRunningAsService": "Сопоставленные сетевые диски недоступны при запуске в качестве службы Windows. См. FAQ для получения дополнительной информации", "No": "Нет", "Yes": "Да", - "AddRemoveOnly": "Добавить и Удалить Только", - "AddNewIndexer": "Добавить Новый Индексатор", - "AddToDownloadClient": "Добавить выпуск в Загрузочный клиент", - "AddedToDownloadClient": "Выпуск добавлен в клиент", - "AddDownloadClientToProwlarr": "Добавление клиента загрузки позволяет {appName} отправлять выпуски прямо из пользовательского интерфейса, выполняя поиск вручную.", - "AddIndexerProxy": "Добавить индексатор прокси", - "Connect": "Оповещения", - "Notification": "Оповещения", + "AddRemoveOnly": "Добавить и удалить только", + "AddNewIndexer": "Добавить новый индексатор", + "AddToDownloadClient": "Добавить релиз в клиент загрузки", + "AddedToDownloadClient": "Релиз добавлен в клиент", + "AddDownloadClientToProwlarr": "Добавление клиента загрузки позволяет {appName} отправлять релизы напрямую из пользовательского интерфейса, во время ручного поиска.", + "AddIndexerProxy": "Добавить прокси для индексатора", + "Connect": "Уведомления", + "Notification": "Уведомление", "Encoding": "Кодирование", "Applications": "Приложения", "Application": "Приложения", - "Notifications": "Оповещения", + "Notifications": "Уведомления", "InstanceName": "Имя экземпляра", - "InstanceNameHelpText": "Имя экземпляра на вкладке и для имени приложения системного журнала", + "InstanceNameHelpText": "Имя экземпляра на вкладке и имя приложения системного журнала", "Started": "Запущено", "Database": "База данных", "Duration": "Длительность", - "ApplicationLongTermStatusCheckSingleClientMessage": "Все индексаторы недоступны из-за ошибок за последние 6 часов: {0}", - "Ended": "Закончился", + "ApplicationLongTermStatusCheckSingleClientMessage": "Все приложения недоступны из-за ошибок более 6 часов: {0}", + "Ended": "Завершён", "LastExecution": "Последнее выполнение", - "NextExecution": "Следующее выполнение", + "NextExecution": "Следующий запуск", "Queued": "В очереди", - "ApplicationLongTermStatusCheckAllClientMessage": "Все индексаторы недоступны из-за ошибок за последние 6 часов", + "ApplicationLongTermStatusCheckAllClientMessage": "Все приложения недоступны из-за ошибок более 6 часов", "LastDuration": "Последняя длительность", - "ThemeHelpText": "Измените тему пользовательского интерфейса приложения, тема «Авто» будет использовать тему вашей ОС для установки светлого или темного режима. Вдохновленный Theme.Park", + "ThemeHelpText": "Изменить тему интерфейса приложения. Тема 'Авто' будет использовать тему вашей ОС для выбора светлого или тёмного режима. Вдохновлено {inspiredBy}.", "Remove": "Удалить", "Replace": "Заменить", - "TheLatestVersionIsAlreadyInstalled": "Последняя версия {appName} уже установлена", + "OnLatestVersion": "Последняя версия {appName} уже установлена", "ApplicationURL": "URL-адрес приложения", "ApplicationUrlHelpText": "Внешний URL-адрес этого приложения, включая http(s)://, порт и базовый URL-адрес", - "Label": "Ярлык", + "Label": "Метка", "ApplyChanges": "Применить изменения", - "ApplyTagsHelpTextRemove": "Удалить: удалить введенные теги", - "DeleteSelectedApplicationsMessageText": "Вы уверены что хотите удалить индексер '{0}'?", + "ApplyTagsHelpTextRemove": "Удалить: Удалить введённые теги", + "DeleteSelectedApplicationsMessageText": "Вы уверены, что хотите удалить выбранные приложения: {count}?", "DeleteSelectedDownloadClients": "Удалить клиент(ы) загрузки", - "DeleteSelectedDownloadClientsMessageText": "Вы уверены, что хотите удалить {count} выбранных клиента загрузки?", - "DeleteSelectedIndexersMessageText": "Вы уверены, что хотите удалить {count} выбранных индексатора?", - "DownloadClientPriorityHelpText": "Установите приоритет нескольких клиентов загрузки. Круговой алгоритм используется для клиентов с таким же приоритетом.", + "DeleteSelectedDownloadClientsMessageText": "Вы уверены, что хотите удалить выбранные клиенты загрузки: {count}?", + "DeleteSelectedIndexersMessageText": "Вы уверены, что хотите удалить выбранные индексаторы: {count}?", + "DownloadClientPriorityHelpText": "Установить приоритет для нескольких клиентов загрузки. Клиенты с одинаковым приоритетом обрабатываются по круговой системе.", "EditSelectedDownloadClients": "Редактировать выбранные клиенты загрузки", - "ApiKeyValidationHealthCheckMessage": "Пожалуйста, обновите свой ключ API, чтобы он был длиной не менее {length} символов. Вы можете сделать это через настройки или файл конфигурации", - "Genre": "Жанры", + "ApiKeyValidationHealthCheckMessage": "Пожалуйста, обновите свой ключ API, чтобы он был длиной не менее {length} символов в длину. Вы можете сделать это через настройки или файл конфигурации", + "Genre": "Жанр", "Theme": "Тема", "Year": "Год", - "ApplyTagsHelpTextAdd": "Добавить: Добавьте теги в существующий список тегов", - "ApplyTagsHelpTextHowToApplyApplications": "Как добавить ярлыки к выбранным фильмам", + "ApplyTagsHelpTextAdd": "Добавить: Добавить теги к существующему списку тегов", + "ApplyTagsHelpTextHowToApplyApplications": "Как применить теги к выбранным приложениям", "ApplyTagsHelpTextHowToApplyIndexers": "Как применить теги к выбранным индексаторам", - "ApplyTagsHelpTextReplace": "Заменить: заменить теги введенными тегами (оставьте поле пустым, чтобы удалить все теги)", - "Track": "След", - "UpdateAvailableHealthCheckMessage": "Доступно новое обновление", - "More": "Более", + "ApplyTagsHelpTextReplace": "Заменить: Заменить теги введёнными тегами (оставьте поле пустым, чтобы удалить все теги)", + "Track": "Трек", + "UpdateAvailableHealthCheckMessage": "Доступно новое обновление: {version}", + "More": "Ещё", "Publisher": "Издатель", - "ConnectionLostReconnect": "Radarr попытается соединиться автоматически или нажмите кнопку внизу.", - "ConnectionLostToBackend": "Radarr потерял связь с сервером и его необходимо перезагрузить, чтобы восстановить работоспособность.", + "ConnectionLostReconnect": "{appName} попытается соединиться автоматически или нажмите кнопку ниже.", + "ConnectionLostToBackend": "{appName} потерял связь с сервером и его необходимо перезагрузить, чтобы восстановить работоспособность.", "RecentChanges": "Последние изменения", "WhatsNew": "Что нового?", - "minutes": "Минуты", - "DeleteAppProfileMessageText": "Вы действительно хотите удалить профиль качества {0}", + "minutes": "минуты", + "DeleteAppProfileMessageText": "Вы уверены, что хотите удалить профиль приложения '{name}'?", "EditDownloadClientImplementation": "Редактировать клиент загрузки - {implementationName}", "EditIndexerImplementation": "Редактировать индексатор - {implementationName}", "NoIndexersFound": "Индексаторы не найдены", - "AuthenticationRequiredHelpText": "Отредактируйте, для каких запросов требуется аутентификация. Не меняйте, пока не поймете все риски.", + "AuthenticationRequiredHelpText": "Отредактируйте, для каких запросов требуется авторизация. Не изменяйте, если не понимаете риски.", "AuthenticationRequired": "Требуется авторизация", - "CountDownloadClientsSelected": "{count} выбранных клиентов загрузки", - "CountIndexersSelected": "{count} выбранных индексаторов", + "CountDownloadClientsSelected": "Выбрано клиентов загрузки: {count}", + "CountIndexersSelected": "Выбрано индексаторов: {count}", "EditSelectedIndexers": "Редактировать выбранный индексатор", - "OnHealthRestored": "При восстановлении работоспособности", - "OnHealthRestoredHelpText": "При восстановлении работоспособности", + "OnHealthRestored": "При восстановлении состояния", + "OnHealthRestoredHelpText": "При восстановлении состояния", "Implementation": "Реализация", - "NoDownloadClientsFound": "Клиенты для загрузки не найдены", + "NoDownloadClientsFound": "Клиенты загрузки не найдены", "IndexerDownloadClientHealthCheckMessage": "Индексаторы с недопустимыми клиентами загрузки: {indexerNames}.", - "StopSelecting": "Прекратить выбор", + "StopSelecting": "Отменить выбор", "AddDownloadClientImplementation": "Добавить клиент загрузки - {implementationName}", "AddConnection": "Добавить подключение", "AddConnectionImplementation": "Добавить подключение - {implementationName}", - "ManageDownloadClients": "Менеджер клиентов загрузки", - "NotificationStatusAllClientHealthCheckMessage": "Все уведомления недоступны из-за сбоев", - "NotificationStatusSingleClientHealthCheckMessage": "Уведомления недоступны из-за сбоев: {notificationNames}", + "ManageDownloadClients": "Управление клиентами загрузки", + "NotificationStatusAllClientHealthCheckMessage": "Все уведомления недоступны из-за ошибок", + "NotificationStatusSingleClientHealthCheckMessage": "Уведомления недоступны из-за ошибок: {notificationNames}", "AddIndexerImplementation": "Добавить индексатор - {implementationName}", - "AddIndexerProxyImplementation": "Добавить индексатор - {implementationName}", - "EditConnectionImplementation": "Добавить соединение - {implementationName}", - "EditIndexerProxyImplementation": "Редактировать индексатор - {implementationName}", + "AddIndexerProxyImplementation": "Добавить прокси для индексатора - {implementationName}", + "EditConnectionImplementation": "Редактировать соединение - {implementationName}", + "EditIndexerProxyImplementation": "Редактировать прокси индексатора - {implementationName}", "Season": "Сезон", - "AddApplicationImplementation": "Добавить соединение - {implementationName}", - "EditApplicationImplementation": "Редактировать уведомление - {implementationName}", - "AuthBasic": "Базовый (всплывающее окно браузера)", + "AddApplicationImplementation": "Добавить приложение - {implementationName}", + "EditApplicationImplementation": "Редактировать приложение - {implementationName}", + "AuthBasic": "Базовый (Всплывающее окно браузера)", "AuthForm": "Формы (Страница авторизации)", "DisabledForLocalAddresses": "Отключено для локальных адресов", - "None": "Ничто", - "ResetAPIKeyMessageText": "Вы уверены, что хотите сбросить Ваш API ключ?", + "None": "Ничего", + "ResetAPIKeyMessageText": "Вы уверены, что хотите сбросить ключ API?", "Categories": "Категории", - "Album": "альбом", + "Album": "Альбом", "AddCustomFilter": "Добавить специальный фильтр", "AuthenticationMethod": "Способ авторизации", "AuthenticationRequiredPasswordHelpTextWarning": "Введите новый пароль", "AuthenticationRequiredUsernameHelpTextWarning": "Введите новое имя пользователя", "RestartProwlarr": "Перезапустить {appName}", - "AuthenticationRequiredWarning": "Чтобы предотвратить удаленный доступ без авторизации, {appName} теперь требует, чтобы авторизация была включена. При желании вы можете отключить авторизацию с локальных адресов.", + "AuthenticationRequiredWarning": "Чтобы предотвратить удалённый доступ без авторизации, {appName} теперь требует включения авторизации. Вы можете опционально отключить авторизацию для локальных адресов.", "Id": "ID", "ManageClients": "Управление клиентами", - "CountApplicationsSelected": "{count} коллекция(ий) выбрано", - "IndexerHDBitsSettingsCodecs": "Кодек", - "IndexerHDBitsSettingsMediums": "Средний", + "CountApplicationsSelected": "Выбрано приложений: {count}", + "IndexerHDBitsSettingsCodecs": "Кодеки", + "IndexerHDBitsSettingsMediums": "Mediums", "Directory": "Каталог", - "CustomFilter": "Настраиваемые фильтры" + "CustomFilter": "Настраиваемый фильтр", + "GrabRelease": "Захватить релиз", + "ManualGrab": "Ручной захват", + "OverrideAndAddToDownloadClient": "Переопределить и добавить в клиент загрузки", + "OverrideGrabModalTitle": "Переопределить и захватить - {title}", + "PrioritySettings": "Приоритет: {priority}", + "SelectDownloadClientModalTitle": "{modalTitle} - Выберите клиент загрузки", + "ProxyValidationBadRequest": "Не удалось протестировать прокси. Код состояния: {statusCode}", + "Default": "По умолчанию", + "AppUpdated": "{appName} обновлён", + "AppUpdatedVersion": "{appName} обновлён до версии `{version}`, для получения последних изменений необходимо перезагрузить {appName}", + "Episode": "Эпизод", + "Donate": "Пожертвовать", + "DownloadClientFreeboxSettingsAppTokenHelpText": "Токен приложения, полученный при создании доступа к Freebox API (например, 'app_token')", + "DownloadClientPneumaticSettingsNzbFolder": "Каталог NZB", + "DownloadClientPneumaticSettingsStrmFolder": "Каталог STRM", + "DownloadClientQbittorrentSettingsContentLayout": "Макет контента", + "DownloadClientQbittorrentSettingsSequentialOrderHelpText": "Загружать последовательно (qBittorrent 4.1.0+)", + "DownloadClientRTorrentSettingsUrlPath": "URL-путь", + "DownloadClientRTorrentSettingsDirectoryHelpText": "Необязательное место для сохранения загрузок, оставьте поле пустым, чтобы использовать расположение rTorrent по умолчанию", + "DownloadClientSettingsAddPaused": "Добавить приостановленные", + "DownloadClientSettingsInitialState": "Начальное состояние", + "DownloadClientTransmissionSettingsUrlBaseHelpText": "Добавляет префикс к URL-адресу RPC {clientName}, например {url}, по умолчанию — '{defaultUrl}'", + "NotificationsTelegramSettingsIncludeAppName": "Включить {appName} в заголовок", + "Category": "Категория", + "Clone": "Клонировать", + "DefaultNameCopiedProfile": "{name} - Копировать", + "DownloadClientFreeboxSettingsHostHelpText": "Имя хоста или IP-адрес хоста Freebox, по умолчанию — '{url}' (будет работать только если находится в одной сети)", + "DownloadClientFreeboxSettingsPortHelpText": "Порт, используемый для доступа к интерфейсу Freebox, по умолчанию — '{port}'", + "DownloadClientPneumaticSettingsNzbFolderHelpText": "Этот каталог должен быть доступен из XBMC", + "DownloadClientQbittorrentSettingsSequentialOrder": "Загружать последовательно", + "DownloadClientSettingsDestinationHelpText": "Ручная установка места загрузки, оставьте пустым для использования значения по умолчанию", + "DownloadClientSettingsInitialStateHelpText": "Начальное состояние для торрентов, добавленных в {clientName}", + "DownloadClientRTorrentSettingsAddStopped": "Добавить остановленные", + "External": "Внешний", + "Destination": "Место назначения", + "BlackholeFolderHelpText": "Каталог, в котором {appName} будет хранить файл {extension}", + "DownloadClientDownloadStationSettingsDirectoryHelpText": "Необязательный общий каталог для сохранения загрузок, оставьте поле пустым, чтобы использовать расположение Download Station по умолчанию", + "DownloadClientFloodSettingsAdditionalTags": "Дополнительные теги", + "DownloadClientFloodSettingsUrlBaseHelpText": "Добавляет префикс к Flood API, такой как {url}", + "DownloadClientFreeboxSettingsApiUrl": "URL-адрес API", + "DownloadClientFreeboxSettingsApiUrlHelpText": "Определите базовый URL-адрес Freebox API с версией API, например '{url}', по умолчанию — '{defaultApiUrl}'", + "DownloadClientFreeboxSettingsAppId": "ID приложения", + "DownloadClientFreeboxSettingsAppIdHelpText": "ID приложения, полученный при создании доступа к Freebox API (например, 'app_id')", + "DownloadClientFreeboxSettingsAppToken": "Токен приложения", + "DownloadClientFloodSettingsAdditionalTagsHelpText": "Добавляет свойства мультимедиа в виде тегов. Подсказки являются примерами.", + "DownloadClientNzbgetSettingsAddPausedHelpText": "Для работы этого параметра требуется версия NzbGet не ниже 16.0", + "DownloadClientPneumaticSettingsStrmFolderHelpText": "Файлы .strm в этом каталоге будут импортированы дроном", + "DownloadClientQbittorrentSettingsFirstAndLastFirst": "Первое и последнее сначала", + "DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText": "Загружать первые и последние части сначала (qBittorrent 4.1.0+)", + "UseSsl": "Использовать SSL", + "AuthenticationMethodHelpTextWarning": "Пожалуйста, выберите допустимый метод авторизации", + "XmlRpcPath": "Путь XML RPC", + "UsenetBlackholeNzbFolder": "Каталог NZB", + "AuthenticationRequiredPasswordConfirmationHelpTextWarning": "Подтвердите новый пароль", + "DownloadClientAriaSettingsDirectoryHelpText": "Необязательное место для сохранения загрузок, оставьте поле пустым, чтобы использовать расположение Aria2 по умолчанию", + "DownloadClientSettingsUrlBaseHelpText": "Добавляет префикс к URL-адресу {clientName}, например, {url}", + "NoHistoryFound": "История не найдена", + "DownloadClientFloodSettingsTagsHelpText": "Начальные теги загрузки. Чтобы быть распознанным, загрузка должна иметь все начальные теги. Это предотвращает конфликты с несвязанными загрузками.", + "DownloadClientQbittorrentSettingsContentLayoutHelpText": "Выбрать расположение контента: настроенное в qBittorrent, исходный макет из торрента или всегда создавать подкаталог (qBittorrent 4.3.2+)", + "DownloadClientQbittorrentSettingsInitialStateHelpText": "Исходное состояние торрентов, добавленных в qBittorrent. Обратите внимание, что принудительные торренты не соблюдают ограничения на раздачу", + "DownloadClientDelugeSettingsUrlBaseHelpText": "Добавляет префикс к URL-адресу json Deluge, см.: {url}", + "NotificationsTelegramSettingsIncludeAppNameHelpText": "При необходимости добавить к заголовку сообщения префикс {appName}, чтобы различать уведомления от разных приложений", + "DownloadClientQbittorrentSettingsUseSslHelpText": "Использовать защищённое соединение. Смотрите 'Параметры' -> 'Веб-интерфейс' -> 'Использовать HTTPS вместо HTTP' в qBittorrent.", + "DownloadClientRTorrentSettingsAddStoppedHelpText": "Включение добавит торренты и магнет-ссылки в rTorrent в остановленном состоянии. Это может привести к повреждению магнет-файлов.", + "DownloadClientRTorrentSettingsUrlPathHelpText": "Путь к конечной точке XMLRPC см. {url}. Обычно это RPC2 или [путь к ruTorrent]{url2} при использовании ruTorrent.", + "DownloadClientSettingsUseSslHelpText": "Использовать защищённое соединение при подключении к {clientName}", + "DownloadClientTransmissionSettingsDirectoryHelpText": "Необязательное место для сохранения загрузок, оставьте поле пустым, чтобы использовать расположение Transmission по умолчанию", + "IndexerSettingsAdditionalParameters": "Дополнительные параметры", + "IndexerSettingsSeedRatio": "Коэффициент раздачи", + "IndexerSettingsSeedTimeHelpText": "Время, в течение которого торрент должен оставаться на раздаче перед остановкой, пусто — используется значение клиента загрузки по умолчанию", + "PasswordConfirmation": "Подтверждение пароля", + "TorrentBlackholeTorrentFolder": "Каталог торрента", + "TorrentBlackholeSaveMagnetFilesHelpText": "Сохранить магнет-ссылку, если файл .torrent недоступен (полезно только в случае, если клиент загрузки поддерживает магнет-ссылки, сохранённые в файле)", + "NotificationsEmailSettingsUseEncryption": "Использовать шифрование", + "NotificationsEmailSettingsUseEncryptionHelpText": "Выбрать режим шифрования: предпочитать шифрование, если оно настроено на сервере; всегда использовать шифрование через SSL (только порт 465) или StartTLS (любой другой порт); никогда не использовать шифрование.", + "LabelIsRequired": "Требуется метка", + "IndexerSettingsSeedTime": "Время сидирования", + "Mixed": "Смешанный", + "IndexerSettingsApiPath": "Путь API", + "IndexerSettingsSeedRatioHelpText": "Рейтинг, которого должен достичь торрент перед остановкой, пусто — используется значение по умолчанию клиента загрузки. Рейтинг должен быть не менее 1,0 и соответствовать правилам индексаторов", + "InvalidUILanguage": "В вашем пользовательском интерфейсе установлен недопустимый язык. Исправьте его и сохраните настройки", + "IndexerHDBitsSettingsCodecsHelpText": "Если не указано, используются все параметры.", + "IndexerHDBitsSettingsMediumsHelpText": "Если не указано, используются все параметры.", + "IndexerSettingsApiPathHelpText": "Путь к API, обычно {url}", + "TorrentBlackholeSaveMagnetFilesExtension": "Сохранить магнет-файлы с расширением", + "TorrentBlackholeSaveMagnetFiles": "Сохранить магнет-файлы", + "IndexerSettingsCookie": "Cookie", + "SecretToken": "Секретный токен", + "TorrentBlackholeSaveMagnetFilesExtensionHelpText": "Расширение для магнет-ссылок, по умолчанию '.magnet'", + "MinimumSeeders": "Минимум сидеров", + "SeedTime": "Время сидирования", + "SeedRatio": "Коэффициент раздачи", + "days": "дни", + "ApplicationSettingsSyncRejectBlocklistedTorrentHashes": "Отклонять хэши торрентов из чёрного списка при захвате", + "Author": "Автор", + "IndexerHDBitsSettingsOriginsHelpText": "Если не указано, используются все параметры.", + "Any": "Любой", + "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "Если торрент заблокирован по хэшу, он может не правильно быть отклонён во время RSS/Поиска для некоторых индексаторов.", + "BuiltIn": "Встроенный", + "ProxyValidationUnableToConnect": "Не удалось подключиться к прокси: {exceptionMessage}. Проверьте журнал ошибок для получения дополнительной информации", + "Script": "Скрипт", + "InfoUrl": "URL-адрес информации", + "PublishedDate": "Дата публикации", + "AllSearchResultsHiddenByFilter": "Все результаты поиска скрыты применённым фильтром.", + "HealthMessagesInfoBox": "Дополнительную информацию о причине появления этих сообщений о проверке работоспособности можно найти, перейдя по ссылке wiki (значок книги) в конце строки или проверить [журналы]({link}). Если у вас возникли трудности с пониманием этих сообщений, вы можете обратиться в нашу службу поддержки по ссылкам ниже.", + "PackageVersionInfo": "{packageVersion} создан {packageAuthor}", + "TotalUserAgentQueries": "Всего запросов User Agent", + "TotalIndexerSuccessfulGrabs": "Всего успешных захватов индексатора", + "TotalIndexerQueries": "Всего запросов к индексатору", + "TVSearchTypes": "Типы поиска ТВ-программ", + "SyncLevelAddRemove": "Только добавление и удаление: При добавлении или удалении индексаторов из {appName} это приложение будет автоматически обновлено.", + "SyncProfiles": "Профили синхронизации", + "TorznabUrl": "URL-адрес Torznab", + "TotalGrabs": "Всего захватов", + "TotalQueries": "Всего запросов", + "TotalHostGrabs": "Всего захватов с хоста", + "TestAllApps": "Тестировать все приложения", + "TotalUserAgentGrabs": "Всего захватов User Agent", + "SyncProfile": "Профиль синхронизации", + "TotalHostQueries": "Всего запросов к хосту", + "SyncLevelFull": "Полная синхронизация: Будет поддерживать индексаторы этого приложения полностью синхронизированными. Изменения, внесённые в индексаторы {appName}, будут синхронизированы с этим приложением. Любые изменения, внесённые в индексаторы удалённо в этом приложении, будут перезаписаны {appName} при следующей синхронизации.", + "Menu": "Меню", + "Artist": "Исполнитель", + "OnGrabHelpText": "При захвате релиза", + "Book": "Книга", + "LogSizeLimit": "Ограничение размера журнала", + "LogSizeLimitHelpText": "Максимальный размер файла журнала в МБ перед архивированием. По умолчанию - 1 МБ.", + "AudioSearch": "Поиск аудио", + "MusicSearchTypes": "Типы поиска музыки", + "AreYouSureYouWantToDeleteCategory": "Вы уверены, что хотите удалить сопоставленную категорию?", + "ClearHistory": "Очистить историю", + "Privacy": "Конфиденциальность", + "EnableIndexer": "Включить индексатор", + "EnableRssHelpText": "Включить RSS-канал для индексатора", + "Stats": "Статистика", + "HistoryDetails": "Подробности истории", + "IndexerDisabled": "Индексатор выключен", + "IndexerFailureRate": "Процент неудач индексатора", + "LastFailure": "Последняя ошибка", + "MassEditor": "Пакетный редактор", + "FullSync": "Полная синхронизация", + "IndexerAuth": "Авторизация индексатора", + "AddApplication": "Добавить приложение", + "AddSyncProfile": "Добавить профиль синхронизации", + "MappedCategories": "Сопоставленные категории", + "Redirect": "Перенаправление", + "ElapsedTime": "Прошедшее время", + "FilterPlaceHolder": "Поисковые индексаторы", + "Private": "Конфиденциальный", + "AdvancedSettingsHiddenClickToShow": "Расширенные настройки скрыты, нажмите, чтобы показать", + "BasicSearch": "Базовый поиск", + "IndexerStatus": "Статус индексатора", + "ManageApplications": "Управление приложениями", + "PackSeedTime": "Время раздачи пакета", + "SearchAllIndexers": "Поиск во всех индексаторах", + "SettingsSqlLoggingHelpText": "Журналировать все SQL-запросы из {appName}", + "DeleteAppProfile": "Удалить профиль приложения", + "EditSyncProfile": "Редактировать профиль синхронизации", + "EnabledRedirected": "Включено, Перенаправлено", + "IndexerProxies": "Прокси индексатора", + "InitialFailure": "Начальный сбой", + "GoToApplication": "Перейти к приложению", + "IndexerName": "Название индексатора", + "NoSearchResultsFound": "Не обнаружены результаты поиска, повторите поиск ниже.", + "QueryOptions": "Параметры запроса", + "SearchTypes": "Типы поиска", + "DeleteIndexerProxy": "Удалить прокси индексатора", + "DeleteSelectedApplications": "Удалить выбранные приложения", + "DevelopmentSettings": "Настройки разработки", + "HistoryCleanup": "Очистка истории", + "SearchCountIndexers": "Поиск в {count} индексаторе(ах)", + "SelectIndexers": "Выберите индексаторы", + "IndexerBeyondHDSettingsApiKeyHelpText": "Ключ API с сайта (Находится в Моя безопасность => Ключ API)", + "IndexerBeyondHDSettingsSearchTypes": "Типы поиска", + "IndexerBeyondHDSettingsSearchTypesHelpText": "Выберите типы релизов, которые вас интересуют. Если ничего не выбрано, используются все варианты.", + "IndexerFileListSettingsPasskeyHelpText": "Passkey сайта (Это алфавитно-цифровая строка в URL трекера, отображаемая в вашем клиенте загрузки)", + "IndexerGazelleGamesSettingsSearchGroupNames": "Поиск названий групп", + "IndexerIPTorrentsSettingsCookieUserAgentHelpText": "User-Agent, используемый с cookie из браузера", + "IndexerIPTorrentsSettingsFreeleechOnlyHelpText": "Искать только релизы с freeleech", + "AreYouSureYouWantToDeleteIndexer": "Вы уверены, что хотите удалить '{name}' из {appName}?", + "BookSearch": "Поиск книг", + "BookSearchTypes": "Типы поиска книг", + "IndexerPassThePopcornSettingsFreeleechOnlyHelpText": "Искать только релизы с freeleech", + "IndexerSettingsGrabLimitHelpText": "Максимальное количество захватов, указанное соответствующей единицей, которое {appName} будет разрешать для сайта", + "IndexerSettingsLimitsUnit": "Единица лимита", + "IndexerSettingsLimitsUnitHelpText": "Единица времени для определения лимитов каждого индексатора", + "IndexerSettingsQueryLimit": "Лимит запросов", + "IndexerSettingsRssKey": "Ключ RSS", + "Parameters": "Параметры", + "RedirectHelpText": "Перенаправить входящий запрос на загрузку для индексатора и передать захват напрямую, вместо проксирования запроса через {appName}.", + "SettingsFilterSentryEvents": "Фильтровать события аналитики", + "NotSupported": "Не поддерживается", + "SyncLevel": "Уровень синхронизации", + "UISettingsSummary": "Параметры для людей с ограниченными возможностями: дата, язык и цветовая схема", + "DeleteApplication": "Удалить приложение", + "Description": "Описание", + "IndexerQuery": "Запрос индексатора", + "IndexerSite": "Сайт индексатора", + "IndexerTagsHelpText": "Используйте теги для указания прокси индексатора или приложений, синхронизированных с индексатором.", + "QueryType": "Тип запроса", + "MovieSearch": "Поиск фильма", + "RssFeed": "Лента RSS", + "AppsMinimumSeeders": "Приложения Минимальное количество сидеров", + "ConnectSettingsSummary": "Уведомления и пользовательские скрипты", + "DeleteClientCategory": "Удалить категорию клиента загрузки", + "IndexerAlreadySetup": "Как минимум один индексатор уже настроен", + "IndexerCategories": "Категории индексатора", + "IndexerProxy": "Прокси индексатора", + "IndexerRss": "RSS индексатора", + "Public": "Публичный", + "SettingsLogRotate": "Ротация журналов", + "SettingsLogRotateHelpText": "Максимальное количество сохраняемых файлов в каталоге журналов", + "DisabledUntil": "Отключено до", + "SearchType": "Тип поиска", + "AppSettingsSummary": "Приложения и настройки для управления взаимодействием {appName} с вашими программами PVR", + "SettingsFilterSentryEventsHelpText": "Фильтровать известные события ошибок пользователя, чтобы они не отправлялись в виде аналитики.", + "MovieSearchTypes": "Типы поиска фильма", + "Apps": "Программы", + "Auth": "Авторизация", + "ClearHistoryMessageText": "Вы уверены, что хотите очистить всю историю {appName}?", + "DeleteSelectedIndexer": "Удалить выбранный индексатор", + "DownloadClientCategory": "Категория клиента загрузки", + "IndexerHDBitsSettingsPasskeyHelpText": "Passkey из пользовательских данных", + "SettingsIndexerLogging": "Включить индексатор", + "RepeatSearch": "Повторить поиск", + "AddCategory": "Добавить категорию", + "AdvancedSettingsShownClickToHide": "Расширенные настройки видны, нажмите, чтобы скрыть", + "AppProfileInUse": "Профиль приложения в использовании", + "IndexerAlphaRatioSettingsExcludeScene": "Исключить SCENE", + "IndexerAlphaRatioSettingsExcludeSceneHelpText": "Исключить релизы SCENE из результатов", + "IndexerBeyondHDSettingsRewindOnly": "Только повторы", + "IndexerDetails": "Подробности индексатора", + "EditCategory": "Редактировать категорию", + "FoundCountReleases": "Найдено релизов: {itemCount}", + "IndexerAlphaRatioSettingsFreeleechOnlyHelpText": "Искать только релизы с freeleech", + "IndexerBeyondHDSettingsLimitedOnly": "Только лимитированные", + "IndexerBeyondHDSettingsRefundOnlyHelpText": "Искать только возвраты", + "IndexerBeyondHDSettingsRewindOnlyHelpText": "Искать только повторы", + "IndexerHistoryLoadError": "Ошибка загрузки истории индексатора", + "IndexerNebulanceSettingsApiKeyHelpText": "Ключ API из Настроек пользователя > Ключи API. Ключ должен иметь права на просмотр и загрузку", + "IndexerPassThePopcornSettingsApiKeyHelpText": "Ключ API сайта", + "SettingsConsoleLogLevel": "Уровень журналирования консоли", + "IndexerSettingsGrabLimit": "Лимит захвата", + "IndexerSettingsVipExpiration": "Дата окончания VIP", + "SearchQueries": "Запросы поиска", + "SeedRatioHelpText": "Коэффициент, которого должен достичь торрент перед остановкой, пусто - значение по умолчанию приложения", + "NoIndexerCategories": "Нет категорий для этого индексатора", + "IndexerVipExpiredHealthCheckMessage": "Привилегии VIP для индексатора истекли: {indexerNames}", + "DefaultCategory": "Категория по умолчанию", + "IndexerHDBitsSettingsFreeleechOnlyHelpText": "Показать только релизы с freeleech", + "IndexerHDBitsSettingsOrigins": "Источники", + "IndexerNzbIndexSettingsApiKeyHelpText": "Ключ API сайта", + "IndexerOrpheusSettingsApiKeyHelpText": "API ключ сайта (Находится в Настройки => Настройки доступа)", + "IndexerSettingsApiUser": "Пользователь API", + "IndexerSettingsAppsMinimumSeeders": "Приложения Минимальное количество сидеров", + "RawSearchSupported": "Поддерживается необработанный поиск", + "RssQueries": "Запросы RSS", + "SearchCapabilities": "Возможности поиска", + "SearchIndexers": "Поисковые индексаторы", + "ActiveIndexers": "Активные индексаторы", + "ApplicationsLoadError": "Не удалось загрузить список приложений", + "IncludeManualGrabsHelpText": "Включить ручные захваты, сделанные внутри {appName}", + "NoApplicationsFound": "Приложения не найдены", + "IndexerHDBitsSettingsUseFilenamesHelpText": "Выберите этот вариант, если хотите использовать имена файлов торрента в качестве названий релизов", + "IndexerHDBitsSettingsUsernameHelpText": "Имя пользователя сайта", + "IndexerSettingsCookieHelpText": "Cookie сайта", + "IndexerSettingsFreeleechOnly": "Только с freeleech", + "IndexerSettingsPasskey": "Pass Key", + "IndexerId": "ID индексатора", + "DeleteSelectedIndexers": "Удалить выбранные индексаторы", + "IndexerGazelleGamesSettingsApiKeyHelpTextWarning": "Требуются разрешения 'Пользователь' и 'Торренты'", + "IndexerNewznabSettingsAdditionalParametersHelpText": "Дополнительные параметры Newznab", + "IndexerNewznabSettingsApiKeyHelpText": "Ключ API сайта", + "IndexerSettingsBaseUrl": "Базовый URL-адрес", + "IndexerSettingsAppsMinimumSeedersHelpText": "Минимальное количество сидеров, необходимое для приложений, чтобы индексатор мог скачать, пустое значение - значение профиля синхронизации по умолчанию", + "PackSeedTimeHelpText": "Время, в течение которого торрент пакета (сезон или дискография) должен оставаться на раздаче перед остановкой, пусто — используется значение приложения по умолчанию", + "IndexerNoDefinitionCheckHealthCheckMessage": "Индексаторы {indexerNames} не имеют определения и не работают. Удалите их из {appName} и добавьте снова.", + "AuthQueries": "Запросы авторизации", + "IndexerNewznabSettingsVipExpirationHelpText": "Введите дату (yyyy-mm-dd) окончания VIP или оставьте поле пустым, {appName} оповестит вас за неделю до окончания VIP", + "IndexerPassThePopcornSettingsApiUserHelpText": "Эти настройки расположены в разделе безопасности PassThePopcorn (Редактировать профиль > Безопасность).", + "IndexerSettingsBaseUrlHelpText": "Выберите основной URL-адрес, который {appName} будет использовать для запросов на сайт", + "Open": "Открыть", + "IndexerInfo": "Информация об индексаторе", + "SettingsLogSql": "Журналировать SQL", + "AverageResponseTimesMs": "Среднее время ответа индексатора (мс)", + "CountIndexersAvailable": "Доступно индексаторов: {count}", + "IndexerSettingsSummary": "Конфигурация глобальных настроек индексатора, включая прокси.", + "IndexerTagsHelpTextWarning": "Теги следует использовать с осторожностью, они могут иметь непредвиденные последствия. Индексатор с тегом будет синхронизироваться только с приложениями, имеющими тот же тег.", + "IndexerTorrentSyndikatSettingsApiKeyHelpText": "Ключ API сайта", + "NoIndexerHistory": "Нет истории для этого индексатора", + "AppsMinimumSeedersHelpText": "Минимальное количество сидеров, необходимое для приложений, чтобы индексатор мог скачать, пустое значение - значение профиля синхронизации по умолчанию", + "DownloadClientsSettingsSummary": "Настройки клиентов загрузки для интеграции в интерфейс поиска {appName}", + "Proxies": "Прокси", + "AppProfileSelectHelpText": "Профили приложения используются для управления настройками RSS, автоматического поиска и интерактивного поиска при синхронизации приложения", + "ProwlarrSupportsAnyDownloadClient": "{appName} совместим с любым из перечисленных ниже клиентом загрузки.", + "Query": "Запрос", + "QueryResults": "Результаты запроса", + "ActiveApps": "Активные приложения", + "ApplicationTagsHelpText": "Синхронизировать индексаторы с этим приложением, которые имеют один или более совпадающих тегов. Если здесь не перечислены теги, то все индексаторы будут синхронизированы без ограничений.", + "ApplicationTagsHelpTextWarning": "Теги следует использовать с осторожностью, они могут иметь непредвиденные последствия. Приложение с тегом будет синхронизироваться только с индексаторами, имеющими тот же тег.", + "IndexerGazelleGamesSettingsApiKeyHelpText": "API ключ сайта (Находится в Настройки => Настройки доступа)", + "IndexerGazelleGamesSettingsSearchGroupNamesHelpText": "Поиск релизов по названию групп", + "IndexerHDBitsSettingsUseFilenames": "Использовать имена файлов", + "IndexerRedactedSettingsApiKeyHelpText": "API ключ сайта (Находится в Настройки => Настройки доступа)", + "IndexerSettingsPackSeedTime": "Время раздачи пакета", + "IndexerSettingsPackSeedTimeIndexerHelpText": "Время, в течение которого торрент пакета (сезон или дискография) должен оставаться на раздаче перед остановкой, пусто — используется значение приложения по умолчанию", + "ClickToChangeQueryOptions": "Нажмите, чтобы изменить параметры запроса", + "DownloadClientSettingsDefaultCategorySubFolderHelpText": "Категория по умолчанию, если для релиза нет соответствующей категории. Создание определённой категории для {appName}, позволяет избежать конфликтов с загрузками, не связанными с {appName}. Использование категории не обязательно, но настоятельно рекомендуется. Создаёт подкаталог [category] в каталоге вывода.", + "AverageQueries": "Среднее количество запросов", + "IndexerGazelleGamesSettingsFreeleechOnlyHelpText": "Искать только релизы с freeleech", + "IndexerMTeamTpSettingsApiKeyHelpText": "Ключ API сайта (Находится в Панели управления пользователя => Безопасность => Лаборатория)", + "IndexerMTeamTpSettingsFreeleechOnlyHelpText": "Искать только релизы с freeleech", + "ProwlarrDownloadClientsAlert": "Если вы намерены выполнять поиск непосредственно в {appName}, вам необходимо добавить клиенты загрузки. В противном случае, добавлять их здесь не нужно. Для поиска из ваших приложений используются клиенты загрузки, которые настроены в самих приложениях.", + "ProwlarrDownloadClientsInAppOnlyAlert": "Клиенты загрузки предназначены только для поиска внутри приложения {appName} и не синхронизируются с другими приложениями. Мы не планируем добавлять функцию синхронизации.", + "ProwlarrSupportsAnyIndexer": "{appName} поддерживает множество индексаторов, а также любой индексатор, использующий стандарт Newznab/Torznab, с помощью 'Generic Newznab' (для Usenet) или 'Generic Torznab' (для торрентов). Выберите и добавьте свой индексатор из списка ниже.", + "Redirected": "Перенаправлено", + "DownloadClientSettingsDefaultCategoryHelpText": "Категория по умолчанию, если для релиза нет соответствующей категории. Создание определённой категории для {appName}, позволяет избежать конфликтов с загрузками, не связанными с {appName}. Использование категории не обязательно, но настоятельно рекомендуется.", + "DownloadClientSettingsPriorityItemHelpText": "Приоритет, используемый при захвате элементов", + "GrabTitle": "Захватить название", + "IndexerBeyondHDSettingsFreeleechOnlyHelpText": "Искать только релизы с freeleech", + "IndexerBeyondHDSettingsLimitedOnlyHelpText": "Искать только с freeleech (Лимитированная отдача)", + "IndexerBeyondHDSettingsRefundOnly": "Только возврат", + "IndexerBeyondHDSettingsRssKeyHelpText": "Ключ RSS с сайта (Находится в Моя безопасность => Ключ RSS)", + "IndexerDownloadClientHelpText": "Определите клиент загрузки, используемый для загрузки из этого индексатора в {appName}.", + "NewznabUrl": "URL-адрес Newznab", + "SeedTimeHelpText": "Время, в течение которого торрент должен оставаться на раздаче перед остановкой, пусто — используется значение приложения по умолчанию", + "IndexerFileListSettingsFreeleechOnlyHelpText": "Искать только релизы с freeleech", + "IndexerFileListSettingsUsernameHelpText": "Имя пользователя сайта", + "IndexerHealthCheckNoIndexers": "Нет включённых индексаторов, {appName} не будет возвращать результаты поиска", + "IndexerObsoleteCheckMessage": "Индексаторы: {0} устарели или были обновлены. Удалите их из {appName} и (или) добавьте снова", + "IndexerSettingsQueryLimitHelpText": "Максимальное количество запросов, указанное соответствующей единицей, которое {appName} будет разрешать для сайта", + "IndexerVipExpiringHealthCheckMessage": "Привилегии VIP для индексатора скоро истекут: {indexerNames}", + "MinimumSeedersHelpText": "Минимальное количество сидеров, необходимое приложению для захвата индексатором", + "SelectedCountOfCountReleases": "Выбрано {selectedCount} из {itemCount} релизов", + "SemiPrivate": "Частично приватный", + "SettingsIndexerLoggingHelpText": "Журналировать дополнительные данные индексатора, включая ответ", + "SyncAppIndexers": "Синхронизировать индексаторы приложения", + "UnableToLoadAppProfiles": "Не удалось загрузить профили приложения", + "Website": "Веб-сайт", + "TvSearch": "Поиск ТВ-программ", + "Url": "URL-адрес", + "UnableToLoadIndexerProxies": "Не удалось загрузить прокси индексатора", + "UnableToLoadDevelopmentSettings": "Не удалось загрузить настройки разработки", + "VipExpiration": "Дата окончания VIP", + "IndexerIPTorrentsSettingsCookieUserAgent": "Cookie User-Agent", + "AverageGrabs": "Среднее количество захватов", + "IndexerSettingsPreferMagnetUrl": "Предпочитать Magnet URL", + "IndexerPassThePopcornSettingsGoldenPopcornOnly": "Только Golden Popcorn", + "IndexerSettingsPreferMagnetUrlHelpText": "При включении этот индексатор предпочтёт использовать для загрузки magnet URL, с возможностью перехода на торрент-ссылки", + "IndexerAvistazSettingsPasswordHelpText": "Пароль веб-сайта", + "IndexerAvistazSettingsPidHelpText": "PID со страницы Мой аккаунт или Мой профиль", + "IndexerAvistazSettingsUsernameHelpTextWarning": "API этого индексатора доступен только для участников и выше рангом.", + "IndexerPassThePopcornSettingsGoldenPopcornOnlyHelpText": "Искать релизы только Golden Popcorn", + "PreferMagnetUrl": "Предпочитать Magnet URL", + "PreferMagnetUrlHelpText": "При включении этот индексатор предпочтёт использовать для загрузки magnet URL, с возможностью перехода на торрент-ссылки", + "IndexerAvistazSettingsFreeleechOnlyHelpText": "Искать только релизы с freeleech", + "IndexerAvistazSettingsUsernameHelpText": "Имя пользователя сайта", + "AptUpdater": "Использовать apt для установки обновления", + "Download": "Загрузить", + "ErrorRestoringBackup": "Ошибка восстановления резервной копии", + "FailedToFetchUpdates": "Не удалось загрузить обновления", + "LogFilesLocation": "Файлы журнала расположены в: {location}", + "Logout": "Завершить сеанс", + "UpdateAppDirectlyLoadError": "Обновление {appName} напрямую невозможно,", + "DockerUpdater": "Обновите контейнер Docker, чтобы получить обновление", + "ExternalUpdater": "{appName} настроен на использование внешнего механизма обновления", + "NoEventsFound": "Событий не найдено", + "RestartReloadNote": "Примечание: {appName} автоматически перезапустится и перезагрузит интерфейс пользователя во время процесса восстановления.", + "TheLogLevelDefault": "Уровень журналирования по умолчанию установлен на 'Информация' и может быть изменён в [Общих настройках](/settings/general)", + "UpdaterLogFiles": "Файлы журнала обновления", + "WouldYouLikeToRestoreBackup": "Хотите восстановить резервную копию '{name}'?", + "Install": "Установить", + "InstallLatest": "Установить последнюю", + "InstallMajorVersionUpdateMessageLink": "Для получения дополнительной информации посетите [{domain}]({url}).", + "InstallMajorVersionUpdate": "Установить обновление", + "InstallMajorVersionUpdateMessage": "Это обновление установит новую версию, которая может не поддерживаться вашей системой. Вы уверены, что хотите установить это обновление?", + "FailedToFetchSettings": "Не удалось загрузить настройки", + "CurrentlyInstalled": "Установлено", + "PreviouslyInstalled": "Ранее установленный", + "DownloadClientUTorrentProviderMessage": "Мы настоятельно советуем не использовать uTorrent, т.к. он известен как программа-шифровальщик и в целом вредоносное ПО." } diff --git a/src/NzbDrone.Core/Localization/Core/sk.json b/src/NzbDrone.Core/Localization/Core/sk.json index 0f2044797..f9fc30a55 100644 --- a/src/NzbDrone.Core/Localization/Core/sk.json +++ b/src/NzbDrone.Core/Localization/Core/sk.json @@ -126,5 +126,27 @@ "AddConnection": "Pridať podmienku", "AddConnectionImplementation": "Pridať pripojenie - {implementationName}", "AddDownloadClientImplementation": "Pridať klienta pre sťahovanie - {implementationName}", - "AddIndexerImplementation": "Pridať Indexer - {implementationName}" + "AddIndexerImplementation": "Pridať Indexer - {implementationName}", + "UnableToAddANewDownloadClientPleaseTryAgain": "Nie je možné pridať novú podmienku, skúste to znova.", + "UnableToAddANewNotificationPleaseTryAgain": "Nie je možné pridať novú podmienku, skúste to znova.", + "UnableToAddANewIndexerPleaseTryAgain": "Nie je možné pridať novú podmienku, skúste to znova.", + "BackupsLoadError": "Nie je možné načítať albumy", + "Docker": "Docker", + "UnableToAddANewApplicationPleaseTryAgain": "Nie je možné pridať novú podmienku, skúste to znova.", + "EditIndexerImplementation": "Pridať Indexer - {implementationName}", + "AddApplicationImplementation": "Pridať pripojenie - {implementationName}", + "EditConnectionImplementation": "Pridať pripojenie - {implementationName}", + "EditDownloadClientImplementation": "Pridať klienta pre sťahovanie - {implementationName}", + "UnableToAddANewIndexerProxyPleaseTryAgain": "Nie je možné pridať novú podmienku, skúste to znova.", + "Directory": "Priečinok", + "IndexerHDBitsSettingsCodecs": "Kodek", + "AddIndexerProxyImplementation": "Pridať Indexer - {implementationName}", + "EditIndexerProxyImplementation": "Pridať Indexer - {implementationName}", + "EditApplicationImplementation": "Pridať podmienku - {implementationName}", + "UnableToAddANewAppProfilePleaseTryAgain": "Nie je možné pridať novú podmienku, skúste to znova.", + "BuiltIn": "Vstavaný", + "AllSearchResultsHiddenByFilter": "Použitý filter skryje všetky výsledky", + "AptUpdater": "Použiť apt pre inštaláciu aktualizácie", + "Discord": "Discord", + "Clone": "Zatvoriť" } diff --git a/src/NzbDrone.Core/Localization/Core/sv.json b/src/NzbDrone.Core/Localization/Core/sv.json index d514a130d..9e7ee1c6c 100644 --- a/src/NzbDrone.Core/Localization/Core/sv.json +++ b/src/NzbDrone.Core/Localization/Core/sv.json @@ -105,7 +105,7 @@ "Level": "Nivå", "KeyboardShortcuts": "Tangentbordsgenvägar", "Info": "Info", - "HealthNoIssues": "Inga problem hittades med din konfiguration", + "NoIssuesWithYourConfiguration": "Inga problem hittades med din konfiguration", "Error": "Fel", "ConnectionLost": "Anslutning saknas", "Component": "Komponent", @@ -114,7 +114,7 @@ "Cancel": "Avbryt", "Apply": "TIllämpa", "Age": "Ålder", - "SystemTimeCheckMessage": "Systemklockan går fel med mer än en dag. Schemalagda uppgifter kan få problem att köras innan tiden är korrigerad", + "SystemTimeHealthCheckMessage": "Systemklockan går fel med mer än en dag. Schemalagda uppgifter kan få problem att köras innan tiden är korrigerad", "HomePage": "Hemsida", "IndexerPriority": "Indexerprioritet", "Reddit": "Reddit", @@ -289,7 +289,7 @@ "Torrent": "Torrenter", "UILanguage": "UI-språk", "UnableToAddANewApplicationPleaseTryAgain": "Det gick inte att lägga till ett nytt meddelande, försök igen.", - "UnableToLoadBackups": "Det gick inte att ladda säkerhetskopior", + "BackupsLoadError": "Det gick inte att ladda säkerhetskopior", "UnableToAddANewIndexerProxyPleaseTryAgain": "Inte möjligt att lägga till en ny indexerare, var god försök igen.", "UnableToLoadGeneralSettings": "Det går inte att läsa in allmänna inställningar", "New": "Ny", @@ -402,7 +402,7 @@ "Queued": "Köad", "Remove": "Ta bort", "Replace": "Ersätta", - "TheLatestVersionIsAlreadyInstalled": "Den senaste versionen av {appName} är redan installerad", + "OnLatestVersion": "Den senaste versionen av {appName} är redan installerad", "ApplicationURL": "Applikations-URL", "ApplicationUrlHelpText": "Denna applikations externa URL inklusive http(s)://, port och URL-bas", "Episode": "Avsnitt", @@ -438,5 +438,28 @@ "DisabledForLocalAddresses": "Inaktiverad för lokala adresser", "None": "Ingen", "ResetAPIKeyMessageText": "Är du säker på att du vill nollställa din API-nyckel?", - "AddCustomFilter": "Lägg till anpassat filter" + "AddCustomFilter": "Lägg till anpassat filter", + "UseSsl": "Använd SSL", + "IndexerHDBitsSettingsMediums": "Medium", + "Directory": "Plats", + "ProxyValidationBadRequest": "Test av proxy misslyckades. Statuskod: {statusCode}", + "CustomFilter": "Anpassade Filter", + "GrabRelease": "Hämta Utågva", + "BuiltIn": "Inbyggd", + "Script": "Skript", + "PublishedDate": "Publiceringsdatum", + "Redirected": "Omdirigera", + "AllSearchResultsHiddenByFilter": "Alla resultat döljs av det tillämpade filtret", + "DockerUpdater": "uppdatera dockerbehållaren för att ta emot uppdateringen", + "ErrorRestoringBackup": "Fel vid återställning av säkerhetskopian", + "NoEventsFound": "Inga händelser hittades", + "RestartReloadNote": "Obs! {appName} startar automatiskt om och laddar om användargränssnittet under återställningsprocessen.", + "UpdateAppDirectlyLoadError": "Det går inte att uppdatera {appName} direkt,", + "AptUpdater": "Använd apt för att installera uppdateringen", + "Download": "Ladda ner", + "ExternalUpdater": "{appName} är konfigurerad för att använda en extern uppdateringsmekanism", + "InstallLatest": "Installera senaste", + "Clone": "Avsluta", + "Mixed": "Fast", + "CurrentlyInstalled": "För närvarande installerad" } diff --git a/src/NzbDrone.Core/Localization/Core/th.json b/src/NzbDrone.Core/Localization/Core/th.json index 4c772c7f5..b157c57d2 100644 --- a/src/NzbDrone.Core/Localization/Core/th.json +++ b/src/NzbDrone.Core/Localization/Core/th.json @@ -99,9 +99,9 @@ "ScriptPath": "เส้นทางสคริปต์", "Seeders": "Seeders", "SelectAll": "เลือกทั้งหมด", - "SystemTimeCheckMessage": "เวลาของระบบปิดมากกว่า 1 วัน งานที่ตั้งเวลาไว้อาจทำงานไม่ถูกต้องจนกว่าจะมีการแก้ไขเวลา", + "SystemTimeHealthCheckMessage": "เวลาของระบบปิดมากกว่า 1 วัน งานที่ตั้งเวลาไว้อาจทำงานไม่ถูกต้องจนกว่าจะมีการแก้ไขเวลา", "UnableToAddANewNotificationPleaseTryAgain": "ไม่สามารถเพิ่มการแจ้งเตือนใหม่โปรดลองอีกครั้ง", - "UnableToLoadBackups": "ไม่สามารถโหลดข้อมูลสำรอง", + "BackupsLoadError": "ไม่สามารถโหลดข้อมูลสำรอง", "UnableToLoadNotifications": "ไม่สามารถโหลดการแจ้งเตือน", "ApplicationStatusCheckAllClientMessage": "รายการทั้งหมดไม่พร้อมใช้งานเนื่องจากความล้มเหลว", "ApplicationStatusCheckSingleClientMessage": "รายการไม่พร้อมใช้งานเนื่องจากความล้มเหลว: {0}", @@ -166,7 +166,7 @@ "Grabbed": "คว้า", "Grabs": "คว้า", "Health": "สุขภาพ", - "HealthNoIssues": "ไม่มีปัญหากับการกำหนดค่าของคุณ", + "NoIssuesWithYourConfiguration": "ไม่มีปัญหากับการกำหนดค่าของคุณ", "HideAdvanced": "ซ่อนขั้นสูง", "History": "ประวัติศาสตร์", "HomePage": "หน้าแรก", @@ -329,7 +329,7 @@ "Queued": "อยู่ในคิว", "Remove": "ลบ", "Replace": "แทนที่", - "TheLatestVersionIsAlreadyInstalled": "มีการติดตั้ง {appName} เวอร์ชันล่าสุดแล้ว", + "OnLatestVersion": "มีการติดตั้ง {appName} เวอร์ชันล่าสุดแล้ว", "Track": "ติดตาม", "DeleteSelectedApplicationsMessageText": "แน่ใจไหมว่าต้องการลบตัวสร้างดัชนี \"{0}\"", "ApplyTagsHelpTextAdd": "เพิ่ม: เพิ่มแท็กในรายการแท็กที่มีอยู่", @@ -358,5 +358,25 @@ "ResetAPIKeyMessageText": "แน่ใจไหมว่าต้องการรีเซ็ตคีย์ API", "RestartProwlarr": "รีสตาร์ท {appName}", "IndexerHDBitsSettingsMediums": "ปานกลาง", - "CustomFilter": "ตัวกรองที่กำหนดเอง" + "CustomFilter": "ตัวกรองที่กำหนดเอง", + "ProxyValidationBadRequest": "ไม่สามารถทดสอบพร็อกซี StatusCode: {statusCode}", + "GrabRelease": "คว้ารีลีส", + "Script": "สคริปต์", + "BuiltIn": "สร้างขึ้นใน", + "PublishedDate": "วันที่เผยแพร่", + "AllSearchResultsHiddenByFilter": "ผลลัพธ์ทั้งหมดถูกซ่อนโดยตัวกรองที่ใช้", + "AptUpdater": "ใช้ apt เพื่อติดตั้งการอัปเดต", + "Download": "ดาวน์โหลด", + "ErrorRestoringBackup": "เกิดข้อผิดพลาดในการกู้คืนข้อมูลสำรอง", + "DockerUpdater": "อัปเดตคอนเทนเนอร์นักเทียบท่าเพื่อรับการอัปเดต", + "ExternalUpdater": "{appName} ถูกกำหนดค่าให้ใช้กลไกการอัพเดตภายนอก", + "NoEventsFound": "ไม่พบกิจกรรม", + "RestartReloadNote": "หมายเหตุ: {appName} จะรีสตาร์ทและโหลด UI ใหม่โดยอัตโนมัติในระหว่างกระบวนการกู้คืน", + "UpdateAppDirectlyLoadError": "ไม่สามารถอัปเดต {appName} ได้โดยตรง", + "InstallLatest": "ติดตั้งล่าสุด", + "Clone": "ปิด", + "Mixed": "แก้ไขแล้ว", + "Stats": "สถานะ", + "CurrentlyInstalled": "ติดตั้งแล้ว", + "Season": "เหตุผล" } diff --git a/src/NzbDrone.Core/Localization/Core/tr.json b/src/NzbDrone.Core/Localization/Core/tr.json index 845ad62cc..0d4af37ac 100644 --- a/src/NzbDrone.Core/Localization/Core/tr.json +++ b/src/NzbDrone.Core/Localization/Core/tr.json @@ -4,19 +4,19 @@ "Dates": "Tarih", "Date": "Tarih", "Connections": "Bağlantılar", - "Connect": "Bağlan", + "Connect": "Bildirimler", "Clear": "Temizle", "Sort": "Sınıflandır", "SetTags": "Etiketleri Ayarla", "Scheduled": "Planlı", - "ProxyResolveIpHealthCheckMessage": "{proxyHostName} Yapılandırılmış Proxy Ana Bilgisayarının IP Adresi çözülemedi", - "ProxyFailedToTestHealthCheckMessage": "Proxy ile test edilemedi: {url}", - "ProxyBadRequestHealthCheckMessage": "Proxy ile test edilemedi. DurumKodu: {statusCode}", + "ProxyResolveIpHealthCheckMessage": "Yapılandırılmış Proxy Ana Bilgisayarı {proxyHostName} için IP Adresi çözümlenemedi", + "ProxyFailedToTestHealthCheckMessage": "Proxy test edilemedi: {url}", + "ProxyBadRequestHealthCheckMessage": "Proxy test edilemedi. Durum kodu: {statusCode}", "Proxy": "Proxy", "Logging": "Loglama", - "LogFiles": "Log dosyaları", - "Host": "Ana bilgisayar", - "GeneralSettingsSummary": "Port, SSL, kullanıcı adı/şifre, proxy, analitikler ve güncellemeler", + "LogFiles": "Log Kayıtları", + "Host": "Sunucu", + "GeneralSettingsSummary": "Port, SSL, kullanıcı adı/şifre, proxy, analiz ve güncellemeler", "Folder": "Klasör", "Files": "Dosyalar", "Filename": "Dosya adı", @@ -25,17 +25,17 @@ "About": "Hakkında", "View": "Görünüm", "Updates": "Güncellemeler", - "UpdateUiNotWritableHealthCheckMessage": "'{uiFolder}' UI klasörü '{userName}' kullanıcısı tarafından yazılamadığından güncelleme yüklenemiyor.", - "UpdateStartupTranslocationHealthCheckMessage": "Başlangıç klasörü '{startupFolder}' bir Uygulama Yer Değiştirme klasöründe olduğu için güncelleme yüklenemiyor.", - "UpdateStartupNotWritableHealthCheckMessage": "'{startupFolder}' başlangıç klasörü '{userName}' kullanıcısı tarafından yazılamadığından güncelleme yüklenemiyor.", + "UpdateUiNotWritableHealthCheckMessage": "UI klasörü '{uiFolder}' '{userName}' kullanıcısı tarafından yazılabilir olmadığından güncelleme yüklenemiyor.", + "UpdateStartupTranslocationHealthCheckMessage": "Başlangıç klasörü '{startupFolder}' bir Uygulama Taşıma klasöründe olduğundan güncelleme yüklenemiyor.", + "UpdateStartupNotWritableHealthCheckMessage": "Başlangıç klasörü '{startupFolder}' '{userName}' kullanıcısı tarafından yazılabilir olmadığından güncelleme yüklenemiyor.", "UnselectAll": "Tüm Seçimleri Kaldır", - "UISettingsSummary": "Takvim, tarih ve renk engelli seçenekler", + "UISettingsSummary": "Takvim, tarih ve renk engelli seçenekleri", "UI": "UI", "Tasks": "Görevler", "TagsSettingsSummary": "Tüm etiketleri ve nasıl kullanıldıklarını göster. Kullanılmayan etiketler kaldırılabilinir", "Tags": "Etiketler", "System": "Sistem", - "Style": "Tarz", + "Style": "Stil", "Status": "Durum", "Size": "Boyut", "ShowAdvanced": "Gelişmiş'i Göster", @@ -47,12 +47,12 @@ "ReleaseStatus": "Yayın Durumu", "ReleaseBranchCheckOfficialBranchMessage": "{0} şubesi geçerli bir {appName} sürüm dalı değil; güncelleme almayacaksınız", "Refresh": "Yenile", - "Queue": "Sırada", + "Queue": "Kuyruk", "Protocol": "Protokol", "Options": "Seçenekler", "NoChanges": "Değişiklikler yok", "NoChange": "Değişiklik yok", - "MoreInfo": "Daha fazla bilgi", + "MoreInfo": "Daha Fazla Bilgi", "LastWriteTime": "Son Yazma Zamanı", "Language": "Dil", "History": "Geçmiş", @@ -63,69 +63,69 @@ "Failed": "Başarısız oldu", "Edit": "Düzenle", "CustomFilters": "Özel Filtreler", - "ConnectSettingsSummary": "Bildirimler, medya sunucularına/oynatıcılara bağlantılar ve özel komut kodları", - "Analytics": "Analitik", + "ConnectSettingsSummary": "Bildirimler ve özel komut dosyaları", + "Analytics": "Analiz", "All": "Hepsi", - "Added": "Eklendi", + "Added": "Eklenme", "Add": "Ekle", "Branch": "Şube", "TestAllClients": "Tüm İstemcileri Test Et", "ErrorLoadingContents": "İçerik yüklenirken hata oluştu", "FeatureRequests": "Özellik talepleri", - "LogLevelTraceHelpTextWarning": "İzleme günlük kaydı yalnızca geçici olarak etkinleştirilmelidir", - "Peers": "Akranlar", + "LogLevelTraceHelpTextWarning": "İzleme kaydı yalnızca geçici olarak etkinleştirilmelidir", + "Peers": "Eşler", "Presets": "Ön ayarlar", "RemoveFilter": "Filtreyi kaldır", "SettingsEnableColorImpairedMode": "Renk Bozukluğu Modunu Etkinleştir", "ShowSearchHelpText": "Fareyle üzerine gelindiğinde arama düğmesini göster", "Shutdown": "Kapat", - "TableOptions": "Masa Seçenekleri", + "TableOptions": "Tablo Seçenekleri", "UnableToLoadTags": "Etiketler yüklenemiyor", "UnsavedChanges": "Kaydedilmemiş Değişiklikler", "Backups": "Yedeklemeler", "BindAddress": "Bind Adresi", - "BypassProxyForLocalAddresses": "Yerel Adresler için Proxy'yi Atla", + "BypassProxyForLocalAddresses": "Yerel Adresler için Proxy'yi Kullanma", "DeleteNotificationMessageText": "'{name}' bildirimini silmek istediğinizden emin misiniz?", "EnableSslHelpText": " Etkili olması için yönetici olarak yeniden çalıştırmayı gerektirir", - "Fixed": "Sabit", + "Fixed": "Düzeltilen", "PendingChangesMessage": "Kaydedilmemiş değişiklikleriniz var, bu sayfadan ayrılmak istediğinizden emin misiniz?", "PendingChangesStayReview": "Kalın ve değişiklikleri inceleyin", - "Port": "Liman", + "Port": "Port No", "PortNumber": "Port numarası", "RestoreBackup": "Yedeği Geri Yükle", "Rss": "RSS", "Save": "Kaydet", "SaveSettings": "Ayarları kaydet", "ScriptPath": "Komut Dosyası Yolu", - "Test": "Sına", + "Test": "Test Et", "TestAll": "Tümünü Test Et", "UnableToAddANewApplicationPleaseTryAgain": "Yeni bir bildirim eklenemiyor, lütfen tekrar deneyin.", "YesCancel": "Evet İptal", "ApplicationStatusCheckAllClientMessage": "Hatalar nedeniyle tüm uygulamalar kullanılamıyor", - "CancelPendingTask": "Bu bekleyen görevi iptal etmek istediğinizden emin misiniz?", + "CancelPendingTask": "Bekleyen görevi iptal etmek istediğinizden emin misiniz?", "DeleteTag": "Etiketi Sil", "BindAddressHelpText": "Tüm arayüzler için geçerli IP adresi, localhost veya '*'", "ConnectSettings": "Bağlantı Ayarları", "DatabaseMigration": "DB Geçişi", - "DeleteApplicationMessageText": "'{0}' bildirimini silmek istediğinizden emin misiniz?", + "DeleteApplicationMessageText": "'{name}' uygulamasını silmek istediğinizden emin misiniz?", "DeleteBackup": "Yedeklemeyi Sil", "DeleteBackupMessageText": "'{name}' yedeğini silmek istediğinizden emin misiniz?", "DeleteDownloadClient": "İndirme İstemcisini Sil", "DeleteDownloadClientMessageText": "'{name}' indirme istemcisini silmek istediğinizden emin misiniz?", - "DownloadClientSettings": "İstemci Ayarlarını İndir", + "DownloadClientSettings": "İndirme İstemcisi Ayarlarını", "EnableAutomaticSearchHelpText": "Kullanıcı arayüzü veya {appName} tarafından otomatik aramalar yapıldığında kullanılacaktır", "ForMoreInformationOnTheIndividualDownloadClients": "Bireysel indirme istemcileri hakkında daha fazla bilgi için bilgi düğmelerine tıklayın.", "Hostname": "Hostname", "OpenThisModal": "Bu Modeli Aç", "Manual": "Manuel", - "Mechanism": "Mekanizma", - "Message": "İleti", + "Mechanism": "Teknik", + "Message": "Mesaj", "MIA": "MIA", "MovieIndexScrollBottom": "Film Dizini: Alta Kaydırma", "MovieIndexScrollTop": "Film Dizini: Yukarı Kaydırma", "NoLinks": "Bağlantı Yok", "PackageVersion": "Paket Versiyonu", - "PageSize": "Sayfa boyutu", + "PageSize": "Sayfa Boyutu", "PageSizeHelpText": "Her sayfada gösterilecek öğe sayısı", "ProxyBypassFilterHelpText": "Ayırıcı olarak \",\" ve \"*\" kullanın. alt alan adları için joker karakter olarak", "ProxyUsernameHelpText": "Gerekirse yalnızca bir kullanıcı adı ve şifre girmeniz gerekir. Aksi takdirde boş bırakın.", @@ -133,30 +133,30 @@ "RemovedFromTaskQueue": "Görev kuyruğundan kaldırıldı", "SendAnonymousUsageData": "Anonim Kullanım Verilerini Gönderin", "Age": "Yıl", - "AllIndexersHiddenDueToFilter": "Uygulanan filtre nedeniyle tüm dizin oluşturucular gizlendi.", + "AllIndexersHiddenDueToFilter": "Uygulanan filtre nedeniyle tüm indeksleyiciler gizlendi.", "AnalyticsEnabledHelpText": "Anonim kullanım ve hata bilgilerini {appName} sunucularına gönderin. Buna, tarayıcınız, hangi {appName} WebUI sayfalarını kullandığınız, hata raporlamanın yanı sıra işletim sistemi ve çalışma zamanı sürümü hakkındaki bilgiler de dahildir. Bu bilgiyi özelliklere ve hata düzeltmelerine öncelik vermek için kullanacağız.", "ApiKey": "API Anahtarı", "AppDataDirectory": "Uygulama Veri Dizini", - "NoUpdatesAreAvailable": "Güncelleme yok", + "NoUpdatesAreAvailable": "Güncelleme bulunamadı", "OAuthPopupMessage": "Pop-up'lar tarayıcınız tarafından engelleniyor", "Ok": "Tamam", "OnHealthIssueHelpText": "Sağlık Sorunu Hakkında", - "BranchUpdate": "{appName}'ı güncellemek için kullanılacak dal", + "BranchUpdate": "{appName} uygulamasını güncellemek için kullanılacak şube", "Close": "Kapat", "ApplicationStatusCheckSingleClientMessage": "Hatalar nedeniyle kullanılamayan uygulamalar: {0}", "ApplyTags": "Etiketleri Uygula", "RssIsNotSupportedWithThisIndexer": "RSS, bu indeksleyici ile desteklenmiyor", "Interval": "Periyot", - "Logs": "Kütükler", + "Logs": "Kayıtlar", "Authentication": "Doğrulama", - "AuthenticationMethodHelpText": "{appName}'a erişmek için Kullanıcı Adı ve Şifre gerektir", + "AuthenticationMethodHelpText": "{appName}'e erişmek için Kullanıcı Adı ve Parola gereklidir", "BackupIntervalHelpText": "Otomatik yedeklemeler arasındaki zaman aralığı", - "BackupNow": "Şimdi yedekle", - "BackupRetentionHelpText": "Saklama süresinden daha eski olan otomatik yedeklemeler otomatik olarak temizlenecektir", + "BackupNow": "Şimdi Yedekle", + "BackupRetentionHelpText": "Saklama süresinden daha eski otomatik yedeklemeler otomatik olarak temizlenecektir", "BeforeUpdate": "Güncellemeden önce", - "BranchUpdateMechanism": "Harici güncelleme mekanizması tarafından kullanılan dal", - "CertificateValidation": "Sertifika Doğrulama", - "CertificateValidationHelpText": "HTTPS sertifika doğrulamasının ne kadar katı olduğunu değiştirin", + "BranchUpdateMechanism": "Harici güncelleme mekanizması tarafından kullanılan şube", + "CertificateValidation": "Sertifika Doğrulaması", + "CertificateValidationHelpText": "HTTPS sertifika doğrulamasının sıkılığını değiştir", "ChangeHasNotBeenSavedYet": "Değişiklik henüz kaydedilmedi", "ClientPriority": "Müşteri Önceliği", "CloneProfile": "Klon Profili", @@ -164,42 +164,42 @@ "Columns": "Sütunlar", "Component": "Bileşen", "ConnectionLost": "Bağlantı koptu", - "DeleteIndexerProxyMessageText": "'{0}' etiketini silmek istediğinizden emin misiniz?", + "DeleteIndexerProxyMessageText": "'{name}' indeksleyici proxy'sini silmek istediğinizden emin misiniz?", "DeleteNotification": "Bildirimi Sil", "DeleteTagMessageText": "'{label}' etiketini silmek istediğinizden emin misiniz?", "Disabled": "Devre dışı", - "Discord": "Uyuşmazlık", - "Docker": "Liman işçisi", - "Donations": "Bağışlar", - "DownloadClient": "İstemciyi İndir", + "Discord": "Discord", + "Docker": "Docker", + "Donations": "Bağış", + "DownloadClient": "İndirme İstemcisi", "DownloadClients": "İndirme İstemcileri", "EnableAutomaticSearch": "Otomatik Aramayı Etkinleştir", "EnableInteractiveSearchHelpText": "Etkileşimli arama kullanıldığında kullanılacak", "FocusSearchBox": "Arama Kutusuna Odaklan", "GeneralSettings": "Genel Ayarlar", - "Grabs": "Kapmak", - "HealthNoIssues": "Yapılandırmanızla ilgili sorun yok", + "Grabs": "İndirenler", + "NoIssuesWithYourConfiguration": "Yapılandırmanızla ilgili sorun yok", "HomePage": "Ana Sayfa", "IllRestartLater": "Daha sonra yeniden başlayacağım", "IncludeHealthWarningsHelpText": "Sağlık Uyarılarını Dahil Et", - "IndexerFlags": "Dizinleyici Bayrakları", - "IndexerLongTermStatusAllUnavailableHealthCheckMessage": "6 saatten uzun süren arızalar nedeniyle tüm dizinleyiciler kullanılamıyor", - "IndexerLongTermStatusUnavailableHealthCheckMessage": "6 saatten uzun süredir yaşanan arızalar nedeniyle dizinleyiciler kullanılamıyor: {indexerNames}", - "IndexerPriority": "Dizinleyici Önceliği", - "IndexerPriorityHelpText": "1 (En Yüksek) ila 50 (En Düşük) arasında Dizin Oluşturucu Önceliği. Varsayılan: 25.", - "IndexerStatusAllUnavailableHealthCheckMessage": "Hatalar nedeniyle tüm dizinleyiciler kullanılamıyor", - "IndexerStatusUnavailableHealthCheckMessage": "Hatalar nedeniyle dizinleyiciler kullanılamıyor: {indexerNames}", + "IndexerFlags": "İndeksleyici Bayrakları", + "IndexerLongTermStatusAllUnavailableHealthCheckMessage": "6 saatten uzun süren hatalar nedeniyle tüm indeksleyiciler kullanılamıyor", + "IndexerLongTermStatusUnavailableHealthCheckMessage": "6 saatten uzun süren hatalar nedeniyle kullanılamayan indeksleyiciler: {indexerNames}", + "IndexerPriority": "İndeksleyici Önceliği", + "IndexerPriorityHelpText": "İndeksleyici Önceliği 1 (En Yüksek) ile 50 (En Düşük) arasında. Varsayılan: 25.", + "IndexerStatusAllUnavailableHealthCheckMessage": "Tüm indeksleyiciler hatalar nedeniyle kullanılamıyor", + "IndexerStatusUnavailableHealthCheckMessage": "Hatalar nedeniyle kullanılamayan indeksleyiciler: {indexerNames}", "Info": "Bilgi", "InteractiveSearch": "Etkileşimli Arama", "KeyboardShortcuts": "Klavye kısayolları", "LaunchBrowserHelpText": " Bir web tarayıcısı açın ve uygulama başlangıcında {appName} ana sayfasına gidin.", - "LogLevel": "Günlük Düzeyi", + "LogLevel": "Log Seviyesi", "Mode": "Mod", "NoTagsHaveBeenAddedYet": "Henüz etiket eklenmedi", "NotificationTriggers": "Bildirim Tetikleyicileri", "OpenBrowserOnStart": "Başlangıçta tarayıcıyı aç", - "Password": "Parola", - "PendingChangesDiscardChanges": "Değişiklikleri atın ve ayrıl", + "Password": "Şifre", + "PendingChangesDiscardChanges": "Değişiklikleri at ve ayrıl", "Priority": "Öncelik", "ProxyType": "Proxy Türü", "ReadTheWikiForMoreInformation": "Daha fazla bilgi için Wiki'yi okuyun", @@ -211,7 +211,7 @@ "RestartNow": "Şimdi yeniden başlat", "Result": "Sonuç", "Retention": "Saklama", - "SettingsEnableColorImpairedModeHelpText": "Renk bozukluğu olan kullanıcıların renk kodlu bilgileri daha iyi ayırt etmesine olanak tanıyan değiştirilmiş stil", + "SettingsEnableColorImpairedModeHelpText": "Renk engelli kullanıcıların renkleri daha iyi ayırt etmesine olanak tanıyan değiştirilmiş stil", "SettingsLongDateFormat": "Uzun Tarih Formatı", "SettingsShortDateFormat": "Kısa Tarih Formatı", "SettingsShowRelativeDates": "Göreli Tarihleri Göster", @@ -226,8 +226,8 @@ "StartTypingOrSelectAPathBelow": "Yazmaya başlayın veya aşağıdan bir yol seçin", "StartupDirectory": "Başlangıç Dizini", "SuggestTranslationChange": "Çeviri değişikliği önerin", - "SystemTimeCheckMessage": "Sistem saati 1 günden fazla kapalı. Zamanlanan görevler, saat düzeltilene kadar doğru çalışmayabilir", - "TableOptionsColumnsMessage": "Hangi sütunların görünür olduğunu ve hangi sırada görüneceklerini seçin", + "SystemTimeHealthCheckMessage": "Sistem saati 1 günden fazla kapalı. Zamanlanan görevler, saat düzeltilene kadar doğru çalışmayabilir", + "TableOptionsColumnsMessage": "Hangi sütunların görünür olacağını ve hangi sırayla görüneceğini seçin", "Title": "Başlık", "Today": "Bugün", "Tomorrow": "Yarın", @@ -239,50 +239,50 @@ "UISettings": "UI Ayarları", "UnableToAddANewAppProfilePleaseTryAgain": "Yeni bir kaliteli profil eklenemiyor, lütfen tekrar deneyin.", "UnableToAddANewDownloadClientPleaseTryAgain": "Yeni bir indirme istemcisi eklenemiyor, lütfen tekrar deneyin.", - "UnableToAddANewIndexerPleaseTryAgain": "Yeni bir dizinleyici eklenemiyor, lütfen tekrar deneyin.", - "UnableToAddANewIndexerProxyPleaseTryAgain": "Yeni bir dizinleyici eklenemiyor, lütfen tekrar deneyin.", + "UnableToAddANewIndexerPleaseTryAgain": "Yeni bir indeksleyici eklenemiyor, lütfen tekrar deneyin.", + "UnableToAddANewIndexerProxyPleaseTryAgain": "Yeni bir indeksleyici eklenemiyor, lütfen tekrar deneyin.", "UnableToAddANewNotificationPleaseTryAgain": "Yeni bir bildirim eklenemiyor, lütfen tekrar deneyin.", - "UnableToLoadBackups": "Yedeklemeler yüklenemiyor", + "BackupsLoadError": "Yedeklemeler yüklenemiyor", "UnableToLoadHistory": "Geçmiş yüklenemiyor", "UnableToLoadNotifications": "Bildirimler yüklenemiyor", "UnableToLoadUISettings": "UI ayarları yüklenemiyor", "Yesterday": "Dün", "AcceptConfirmationModal": "Onay Modunu Kabul Et", - "AddIndexer": "Dizinleyici Ekle", + "AddIndexer": "İndeksleyici Ekle", "AddDownloadClient": "İndirme İstemcisi Ekle", "AddingTag": "Etiket ekleniyor", "CouldNotConnectSignalR": "SignalR'ye bağlanılamadı, kullanıcı arayüzü güncellenmeyecek", "Custom": "Özel", - "DownloadClientStatusSingleClientHealthCheckMessage": "Hatalar nedeniyle indirilemeyen istemciler: {downloadClientNames}", + "DownloadClientStatusSingleClientHealthCheckMessage": "Hatalar nedeniyle indirme istemcileri kullanılamıyor: {downloadClientNames}", "Enabled": "Etkin", "IgnoredAddresses": "Yoksayılan Adresler", - "Indexer": "Dizinleyici", - "DownloadClientStatusAllClientHealthCheckMessage": "Hatalar nedeniyle tüm indirme istemcileri kullanılamıyor", - "EditIndexer": "Dizinleyiciyi Düzenle", - "Enable": "etkinleştirme", + "Indexer": "İndeksleyici", + "DownloadClientStatusAllClientHealthCheckMessage": "Tüm indirme istemcileri hatalar nedeniyle kullanılamıyor", + "EditIndexer": "İndeksleyiciyi Düzenle", + "Enable": "Etkinleştir", "EnableInteractiveSearch": "Etkileşimli Aramayı Etkinleştir", "EnableRss": "RSS'yi etkinleştir", "EnableSSL": "SSL'yi etkinleştir", "NoLeaveIt": "Hayır, Bırak", "Error": "Hata", - "Events": "Etkinlikler", + "Events": "Olaylar", "EventType": "Etkinlik tipi", "Exception": "İstisna", "ExistingTag": "Mevcut etiket", - "IndexerProxyStatusAllUnavailableHealthCheckMessage": "Hatalar nedeniyle tüm dizinleyiciler kullanılamıyor", - "IndexerProxyStatusUnavailableHealthCheckMessage": "Hatalar nedeniyle dizinleyiciler kullanılamıyor: {indexerProxyNames}", - "Indexers": "Dizinleyiciler", + "IndexerProxyStatusAllUnavailableHealthCheckMessage": "Hatalar nedeniyle tüm indeksleyiciler kullanılamıyor", + "IndexerProxyStatusUnavailableHealthCheckMessage": "Hatalar nedeniyle indeksleyiciler kullanılamıyor: {indexerProxyNames}", + "Indexers": "İndeksleyiciler", "Name": "İsim", "New": "Yeni", "NoBackupsAreAvailable": "Kullanılabilir yedek yok", - "NoLogFiles": "Günlük dosyası yok", + "NoLogFiles": "Log kayıt dosyası henüz oluşturulmadı", "Restart": "Tekrar başlat", "RestartRequiredHelpTextWarning": "Etkili olması için yeniden başlatma gerektirir", "Restore": "Onarmak", "Seeders": "Ekme makineleri", "TagCannotBeDeletedWhileInUse": "Kullanımdayken silinemez", "TagIsNotUsedAndCanBeDeleted": "Etiket kullanılmaz ve silinebilir", - "TagsHelpText": "En az bir eşleşen etikete sahip filmler için geçerlidir", + "TagsHelpText": "En az bir eşleşen etiketi olan indeksleyiciler için geçerlidir", "UILanguage": "UI Dili", "UpdateScriptPathHelpText": "Çıkarılan bir güncelleme paketini alan ve güncelleme işleminin geri kalanını işleyen özel bir komut dosyasına giden yol", "Uptime": "Çalışma süresi", @@ -295,10 +295,10 @@ "Warn": "Uyar", "Wiki": "Wiki", "Apply": "Uygula", - "BackupFolderHelpText": "Göreli yollar {appName}'ın AppData dizini altında olacaktır", - "Grabbed": "Yakalandı", + "BackupFolderHelpText": "Bağıl yollar {appName}'ın AppData dizini altında olacak", + "Grabbed": "Alındı", "ProxyPasswordHelpText": "Gerekirse yalnızca bir kullanıcı adı ve şifre girmeniz gerekir. Aksi takdirde boş bırakın.", - "UpdateAutomaticallyHelpText": "Güncellemeleri otomatik olarak indirin ve yükleyin. Yine de Sistem'den yükleyebileceksiniz: Güncellemeler", + "UpdateAutomaticallyHelpText": "Güncelleştirmeleri otomatik olarak indirip yükleyin. Sistem: Güncellemeler'den yükleme yapmaya devam edebileceksiniz", "UpdateMechanismHelpText": "{appName}'ın yerleşik güncelleyicisini veya bir komut dosyasını kullanın", "ShowSearch": "Aramayı Göster", "DownloadClientsLoadError": "İndirme istemcileri yüklenemiyor", @@ -313,76 +313,76 @@ "HistoryCleanupDaysHelpText": "Otomatik temizlemeyi devre dışı bırakmak için 0'a ayarlayın", "HistoryCleanupDaysHelpTextWarning": "Geri dönüşüm kutusundaki, seçilen gün sayısından daha eski olan dosyalar otomatik olarak temizlenecektir", "Filters": "Filtreler", - "OnGrab": "Yakalandığında", + "OnGrab": "Yayın Alındığında", "OnHealthIssue": "Sağlık Sorunu Hakkında", - "TestAllIndexers": "Tüm Dizinleyicileri Test Et", - "GrabReleases": "Bırakma", + "TestAllIndexers": "İndeksleyicileri Test Et", + "GrabReleases": "Sürümü Al", "No": "Hayır", "NetCore": ".NET", - "UnableToLoadIndexers": "Dizinleyiciler yüklenemiyor", + "UnableToLoadIndexers": "İndeksleyiciler yüklenemiyor", "Yes": "Evet", "Link": "Bağlantılar", - "MappedDrivesRunningAsService": "Eşlenen ağ sürücüleri, bir Windows Hizmeti olarak çalışırken kullanılamaz. Daha fazla bilgi için lütfen SSS bölümüne bakın", + "MappedDrivesRunningAsService": "Windows Hizmeti olarak çalıştırıldığında eşlenen ağ sürücüleri kullanılamaz. Daha fazla bilgi için lütfen SSS'ye bakın", "Ended": "Bitti", - "LastDuration": "Yürütme Süresi", + "LastDuration": "Son Süre", "LastExecution": "Son Yürütme", "NextExecution": "Sonraki Yürütme", - "Queued": "Kuyruğa alındı", - "ApplicationLongTermStatusCheckAllClientMessage": "6 saatten uzun süren arızalar nedeniyle tüm dizinleyiciler kullanılamıyor", - "ApplicationLongTermStatusCheckSingleClientMessage": "6 saatten uzun süredir yaşanan arızalar nedeniyle dizinleyiciler kullanılamıyor: {0}", + "Queued": "Kuyrukta", + "ApplicationLongTermStatusCheckAllClientMessage": "6 saatten uzun süren arızalar nedeniyle tüm indeksleyiciler kullanılamıyor", + "ApplicationLongTermStatusCheckSingleClientMessage": "6 saatten uzun süredir yaşanan arızalar nedeniyle indeksleyiciler kullanılamıyor: {0}", "Remove": "Kaldır", - "Replace": "Değiştir", - "TheLatestVersionIsAlreadyInstalled": "{appName}'ın en son sürümü zaten kurulu", - "ApplyTagsHelpTextAdd": "Ekle: Etiketleri mevcut etiket listesine ekleyin", - "ApplyTagsHelpTextHowToApplyApplications": "Seçilen filmlere etiketler nasıl uygulanır", - "ApplyTagsHelpTextRemove": "Kaldır: Girilen etiketleri kaldırın", - "ApplyTagsHelpTextHowToApplyIndexers": "Seçilen indeksleyicilere etiketler nasıl uygulanır?", - "ApplyTagsHelpTextReplace": "Değiştir: Etiketleri girilen etiketlerle değiştirin (tüm etiketleri kaldırmak için etiket girmeyin)", + "Replace": "Yer Değiştir", + "OnLatestVersion": "{appName}'ın en son sürümü kurulu", + "ApplyTagsHelpTextAdd": "Ekle: Mevcut etiket listesine etiketleri ekleyin", + "ApplyTagsHelpTextHowToApplyApplications": "Seçili uygulamalara etiketler nasıl uygulanır?", + "ApplyTagsHelpTextRemove": "Kaldır: Girilen etiketleri kaldır", + "ApplyTagsHelpTextHowToApplyIndexers": "Seçilen indeksleyicilere etiketler nasıl uygulanır", + "ApplyTagsHelpTextReplace": "Değiştir: Etiketleri girilen değerlerde değiştirin (tüm etiketleri kaldırmak için etiket girmeyin)", "DeleteSelectedDownloadClients": "İndirme İstemcilerini Sil", - "DownloadClientPriorityHelpText": "Birden çok İndirme İstemcisine öncelik verin. Round-Robin, aynı önceliğe sahip müşteriler için kullanılır.", - "Genre": "Türler", + "DownloadClientPriorityHelpText": "Birden fazla İndirme İstemcisine öncelik verin. Aynı önceliğe sahip istemciler için Round-Robin algoritması kullanılır.", + "Genre": "Tür", "Track": "İzleme", "Year": "Yıl", "More": "Daha", - "DeleteAppProfileMessageText": "Kalite profilini silmek istediğinizden emin misiniz {0}", - "RecentChanges": "Son değişiklikler", + "DeleteAppProfileMessageText": "'{name}' uygulama profilini silmek istediğinizden emin misiniz?", + "RecentChanges": "Son Değişiklikler", "minutes": "Dakika", - "WhatsNew": "Ne var ne yok?", - "ConnectionLostReconnect": "{appName} otomatik bağlanmayı deneyecek veya aşağıda yeniden yükle seçeneğini işaretleyebilirsiniz.", + "WhatsNew": "Neler Yeni?", + "ConnectionLostReconnect": "{appName} otomatik olarak bağlanmayı deneyecek veya aşağıdaki yeniden yükle butonuna tıklayabilirsiniz.", "NotificationStatusAllClientHealthCheckMessage": "Arızalar nedeniyle tüm bildirimler kullanılamıyor", "NotificationStatusSingleClientHealthCheckMessage": "Arızalar nedeniyle bildirimler kullanılamıyor: {notificationNames}", "Applications": "Uygulamalar", "AuthBasic": "Temel (Tarayıcı Açılır Penceresi)", - "AuthForm": "Formlar (Giriş Sayfası)", - "DisabledForLocalAddresses": "Yerel Adresler için Devre Dışı Bırakıldı", + "AuthForm": "Form (Giriş Sayfası)", + "DisabledForLocalAddresses": "Yerel Adresler için Devre Dışı", "None": "Yok", "ResetAPIKeyMessageText": "API Anahtarınızı sıfırlamak istediğinizden emin misiniz?", "Categories": "Kategoriler", "Application": "Uygulama", - "Episode": "bölüm", + "Episode": "Bölüm", "AddConnection": "Bağlantı Ekle", "AddApplicationImplementation": "Uygulama Ekle - {implementationName}", - "AddIndexerImplementation": "Yeni Dizin Ekle - {implementationName}", - "AddIndexerProxyImplementation": "Koşul Ekle - {implementationName}", - "EditConnectionImplementation": "Koşul Ekle - {implementationName}", + "AddIndexerImplementation": "İndeksleyici Ekle - {implementationName}", + "AddIndexerProxyImplementation": "İndeksleyici Proxy'sini Ekle - {implementationName}", + "EditConnectionImplementation": "Bildirimi Düzenle - {implementationName}", "AddConnectionImplementation": "Bağlantı Ekle - {implementationName}", - "RestartProwlarr": "{appName}'ı yeniden başlatın", - "EditApplicationImplementation": "Koşul Ekle - {implementationName}", - "EditIndexerImplementation": "Koşul Ekle - {implementationName}", - "EditIndexerProxyImplementation": "Koşul Ekle - {implementationName}", + "RestartProwlarr": "{appName}'ı Yeniden Başlat", + "EditApplicationImplementation": "Uygulamayı Düzenle - {implementationName}", + "EditIndexerImplementation": "İndeksleyiciyi Düzenle - {implementationName}", + "EditIndexerProxyImplementation": "İndeksleyici Proxy'sini Düzenle - {implementationName}", "AddCustomFilter": "Özel Filtre Ekleyin", "AddDownloadClientImplementation": "İndirme İstemcisi Ekle - {implementationName}", "EditDownloadClientImplementation": "İndirme İstemcisini Düzenle - {implementationName}", "ApplicationURL": "Uygulama URL'si", - "AuthenticationRequired": "Kimlik Doğrulama Gerekli", + "AuthenticationRequired": "Kimlik Doğrulama", "ApplyChanges": "Değişiklikleri Uygula", "CountDownloadClientsSelected": "{count} indirme istemcisi seçildi", - "CountIndexersSelected": "{count} dizinleyici seçildi", + "CountIndexersSelected": "{count} indeksleyici seçildi", "AuthenticationRequiredWarning": "Kimlik doğrulaması olmadan uzaktan erişimi engellemek için, {appName}'da artık kimlik doğrulamanın etkinleştirilmesini gerektiriyor. İsteğe bağlı olarak yerel adresler için kimlik doğrulamayı devre dışı bırakabilirsiniz.", "Clone": "Klon", "Category": "Kategori", "AppUpdated": "{appName} Güncellendi", - "AppUpdatedVersion": "{appName}, `{version}` sürümüne güncellendi; en son değişikliklerin etkin olabilmesi için {appName} uygulamasını yeniden başlatmanız gerekli", + "AppUpdatedVersion": "{appName}, `{version}` sürümüne güncellendi; en son değişikliklerin etkin olabilmesi için {appName} uygulamasını yeniden başlatmanız gereklidir", "ApplicationUrlHelpText": "Bu uygulamanın http(s)://, bağlantı noktası ve URL tabanını içeren harici URL'si", "AuthenticationRequiredPasswordConfirmationHelpTextWarning": "Yeni şifreyi onayla", "AuthenticationRequiredPasswordHelpTextWarning": "Yeni şifre girin", @@ -398,11 +398,11 @@ "AddDownloadClientToProwlarr": "İndirme istemcisi eklemek, görsel arayüz üzerinde manuel arama yaparak indirilecek içeriği {appName} uygulamasına direkt olarak eklemenize olanak sağlar.", "AddApplication": "Uygulama Ekle", "AddCategory": "Kategori Ekle", - "AddNewIndexer": "Yeni Dizin Oluşturucu Ekle", + "AddNewIndexer": "Yeni İndekleyici Ekle", "ActiveApps": "Aktif Uygulamalar", - "ActiveIndexers": "Aktif Dizin Oluşturucular", + "ActiveIndexers": "Aktif İndeksleyiciler", "AdvancedSettingsHiddenClickToShow": "Gelimiş ayarlar gizli, göstermek için tıklayın", - "AddIndexerProxy": "Dizin Oluşturucu Vekili Ekle", + "AddIndexerProxy": "İndeksleyici Proxy'sini Ekle", "AddedToDownloadClient": "İçerik istemciye eklendi", "Album": "Albüm", "AdvancedSettingsShownClickToHide": "Gelişmiş ayarlar gösteriliyor, gizlemek için tıklayın", @@ -410,7 +410,7 @@ "DownloadClientAriaSettingsDirectoryHelpText": "İndirilenlerin yerleştirileceği isteğe bağlı konum, varsayılan Aria2 konumunu kullanmak için boş bırakın", "Donate": "Bağış yap", "Destination": "Hedef", - "Directory": "Rehber", + "Directory": "Dizin", "DownloadClientDownloadStationSettingsDirectoryHelpText": "İndirilenlerin yerleştirileceği isteğe bağlı paylaşımlı klasör, varsayılan Download Station konumunu kullanmak için boş bırakın", "DownloadClientFloodSettingsAdditionalTags": "Ek Etiketler", "DownloadClientDelugeSettingsUrlBaseHelpText": "Deluge json URL'sine bir önek ekler, bkz. {url}", @@ -420,21 +420,21 @@ "Database": "Veri tabanı", "DefaultNameCopiedProfile": "{name} - Kopyala", "DeleteSelectedDownloadClientsMessageText": "Seçilen {count} indirme istemcisini silmek istediğinizden emin misiniz?", - "DeleteSelectedIndexersMessageText": "Seçilen {count} dizinleyiciyi silmek istediğinizden emin misiniz?", + "DeleteSelectedIndexersMessageText": "Seçilen {count} indeksleyiciyi silmek istediğinizden emin misiniz?", "DownloadClientFreeboxSettingsPortHelpText": "Freebox arayüzüne erişim için kullanılan bağlantı noktası, varsayılan olarak '{port}' şeklindedir", "ApiKeyValidationHealthCheckMessage": "Lütfen API anahtarınızı en az {length} karakter sayısı kadar güncelleyiniz. Bunu ayarlar veya yapılandırma dosyası üzerinden yapabilirsiniz", "AppProfileInUse": "Kullanımda Olan Uygulama Profili", - "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "Eğer bir torrent hash değeri bazlı engellendi ise bazı dizin oluşturucular RSS/Arama sırasında bu torrenti gerektiği gibi reddedemeyebilir, bunu aktif etmek torrentin çekildikten sonra reddedilebilmesine izin verecektir, ama istemciye gönderilmeden önce.", + "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "Bir torrent, hash ile engellenirse bazı indeksleyiciler için RSS/Arama sırasında düzgün bir şekilde reddedilemeyebilir; bu özelliğin etkinleştirilmesi, torrent yakalandıktan sonra ancak istemciye gönderilmeden önce reddedilmesine olanak tanır..", "AppProfileSelectHelpText": "Uygulama profilleri, Uygulama eşitlemede RSS, Otomatik Arama ve İnteraktif Arama ayarlarını kontrol etmek için kullanılır", - "ApplicationSettingsSyncRejectBlocklistedTorrentHashes": "İçerik eklenirken eşitleme ret işlemi, Torrent hash değerlerini kara listeye aldı", - "AppSettingsSummary": "{appName} uygulamasının PVR progranlarına müdahele etmek için gerekli konfigürasyonda kullanılan uygulama ve ayarlar", + "ApplicationSettingsSyncRejectBlocklistedTorrentHashes": "Alma Sırasında Engellenen Torrent Karmalarını Eşitle ve Reddet", + "AppSettingsSummary": "{appName} uygulamasının PVR programlarınızla nasıl etkileşime gireceğini yapılandırmak için uygulamalar ve ayarlar", "DownloadClientFreeboxSettingsApiUrl": "API URL'si", "DownloadClientFreeboxSettingsApiUrlHelpText": "Freebox API temel URL'sini API sürümüyle tanımlayın, örneğin '{url}', varsayılan olarak '{defaultApiUrl}' olur", "DownloadClientFreeboxSettingsAppIdHelpText": "Freebox API'sine erişim oluşturulurken verilen uygulama kimliği (ör. 'app_id')", - "DownloadClientFreeboxSettingsAppToken": "Uygulama Jetonu", - "DownloadClientFreeboxSettingsAppTokenHelpText": "Freebox API'sine erişim oluşturulurken alınan uygulama jetonu (ör. 'app_token')", + "DownloadClientFreeboxSettingsAppToken": "Uygulama Token'ı", + "DownloadClientFreeboxSettingsAppTokenHelpText": "Freebox API'sine erişim oluşturulurken alınan uygulama token'ı (ör. 'app_token')", "DownloadClientFreeboxSettingsAppId": "Uygulama kimliği", - "DownloadClientFreeboxSettingsHostHelpText": "Freebox'un ana bilgisayar adı veya ana bilgisayar IP adresi, varsayılan olarak '{url}' şeklindedir (yalnızca aynı ağdaysa çalışır)", + "DownloadClientFreeboxSettingsHostHelpText": "Freebox'un istemci adı veya istemci IP adresi, varsayılan olarak '{url}' şeklindedir (yalnızca aynı ağda çalışır)", "Duration": "Süre", "DownloadClientQbittorrentSettingsContentLayoutHelpText": "qBittorrent'in yapılandırılmış içerik düzenini mi, torrentteki orijinal düzeni mi kullanacağınızı yoksa her zaman bir alt klasör oluşturup oluşturmayacağınızı (qBittorrent 4.3.2+)", "DownloadClientQbittorrentSettingsInitialStateHelpText": "Torrentlerin başlangıç durumu qBittorrent'e eklendi. Zorunlu Torrentlerin seed kısıtlamalarına uymadığını unutmayın", @@ -443,9 +443,9 @@ "DownloadClientSettingsInitialState": "Başlangıç Durumu", "DownloadClientSettingsUrlBaseHelpText": "{clientName} URL'sine {url} gibi bir önek ekler", "DownloadClientSettingsInitialStateHelpText": "{clientName} dosyasına eklenen torrentler için başlangıç durumu", - "DownloadClientQbittorrentSettingsFirstAndLastFirst": "İlk ve Son İlk", + "DownloadClientQbittorrentSettingsFirstAndLastFirst": "İlk ve Son", "DownloadClientRTorrentSettingsAddStoppedHelpText": "Etkinleştirme, durdurulmuş durumdaki rTorrent'e torrentler ve magnet ekleyecektir. Bu magnet dosyalarını bozabilir.", - "DownloadClientSettingsAddPaused": "Ekleme Durduruldu", + "DownloadClientSettingsAddPaused": "Duraklatılana Ekle", "DownloadClientSettingsDestinationHelpText": "İndirme hedefini manuel olarak belirtir, varsayılanı kullanmak için boş bırakın", "DownloadClientSettingsUseSslHelpText": "{clientName} ile bağlantı kurulurken güvenli bağlantıyı kullan", "DownloadClientTransmissionSettingsDirectoryHelpText": "İndirilenlerin yerleştirileceği isteğe bağlı konum, varsayılan İletim konumunu kullanmak için boş bırakın", @@ -461,11 +461,11 @@ "DownloadClientPneumaticSettingsNzbFolderHelpText": "Bu klasöre XBMC'den erişilmesi gerekecek", "DownloadClientQbittorrentSettingsSequentialOrderHelpText": "Sıralı olarak indirin (qBittorrent 4.1.0+)", "DownloadClientQbittorrentSettingsUseSslHelpText": "Güvenli bir bağlantı kullanın. qBittorrent'te Seçenekler -> Web Kullanıcı Arayüzü -> 'HTTP yerine HTTPS kullan' bölümüne bakın.", - "DownloadClientRTorrentSettingsAddStopped": "Ekleme Durduruldu", + "DownloadClientRTorrentSettingsAddStopped": "Durdurulana Ekle", "Label": "Etiket", "EditSelectedDownloadClients": "Seçilen İndirme İstemcilerini Düzenle", - "EditSelectedIndexers": "Seçili Dizinleyicileri Düzenle", - "NoIndexersFound": "Dizinleyici bulunamadı", + "EditSelectedIndexers": "Seçili İndeksleyicileri Düzenle", + "NoIndexersFound": "İndeksleyici bulunamadı", "NoHistoryFound": "Geçmiş bulunamadı", "ManageDownloadClients": "İndirme İstemcilerini Yönet", "InstanceNameHelpText": "Sekmedeki örnek adı ve Syslog uygulaması adı için", @@ -486,14 +486,326 @@ "UseSsl": "SSL kullan", "OnApplicationUpdate": "Uygulama Güncellemesinde", "TorrentBlackholeSaveMagnetFiles": "Magnet Dosyalarını Kaydet", - "SecretToken": "Gizlilik Jetonu", + "SecretToken": "Gizlilik Token'ı", "TorrentBlackholeSaveMagnetFilesExtension": "Magnet Dosya Uzantısını Kaydet", "TorrentBlackholeSaveMagnetFilesExtensionHelpText": "Magnet bağlantıları için kullanılacak uzantı, varsayılan olarak '.magnet'tir", "TorrentBlackholeSaveMagnetFilesHelpText": ".torrent dosyası yoksa magnet bağlantısını kaydedin (yalnızca indirme istemcisi bir dosyaya kaydedilen magnetleri destekliyorsa kullanışlıdır)", "TorrentBlackholeTorrentFolder": "Torrent Klasörü", - "PasswordConfirmation": "Şifre onayı", + "PasswordConfirmation": "Şifre Tekrarı", "StopSelecting": "Düzenlemeden Çık", "Started": "Başlatıldı", "UsenetBlackholeNzbFolder": "Nzb Klasörü", - "XmlRpcPath": "XML RPC Yolu" + "XmlRpcPath": "XML RPC Yolu", + "IndexerSettingsSeedTime": "Seed Süresi", + "IndexerSettingsSeedRatio": "Seed Oranı", + "IndexerSettingsSeedTimeHelpText": "Bir torrentin durdurulmadan önce ulaşması gereken oran, boş bırakıldığında uygulamanın varsayılanı kullanılır", + "IndexerSettingsSeedRatioHelpText": "Bir torrentin durdurulmadan önce ulaşması gereken oran. Boş bırakılırsa indirme istemcisinin varsayılan değerini kullanır. Oran en az 1,0 olmalı ve indeksleyici kurallarına uygun olmalıdır", + "External": "Harici", + "Notifications": "Bildirimler", + "CountApplicationsSelected": "{count} uygulama seçildi", + "SeedRatio": "Seed Oranı", + "ThemeHelpText": "Uygulama UI Temasını Değiştirin, 'Otomatik' Tema, işletim sisteminizde kullandığınız Açık veya Koyu moda göre ayarlanır . {inspiredBy} tarafından esinlenilmiştir.", + "Notification": "Bildirimler", + "SelectDownloadClientModalTitle": "{modalTitle} - İndirme İstemcisini Seçin", + "EditSyncProfile": "Senkronizasyon Profilini Düzenle", + "UserAgentProvidedByTheAppThatCalledTheAPI": "API'yi çağıran uygulama tarafından sağlanan Kullanıcı Aracısı", + "Author": "Yazar", + "IndexerHDBitsSettingsMediums": "Medyalar", + "OnHealthRestoredHelpText": "Sağlığın İyileştirilmesi Hakkında", + "SeedTime": "Seed Süresi", + "IndexerHDBitsSettingsCodecs": "Kodekler", + "Publisher": "Yayımcı", + "OnApplicationUpdateHelpText": "Uygulama Güncellemesinde", + "DeleteSelectedApplicationsMessageText": "Seçili {count} uygulamayı silmek istediğinizden emin misiniz?", + "ProxyValidationBadRequest": "Proxy ile test edilemedi. DurumKodu: {statusCode}", + "UpdateAvailableHealthCheckMessage": "Yeni güncelleme mevcut: {version}", + "days": "gün", + "Default": "Varsayılan", + "GrabRelease": "Sürümü Al", + "ManualGrab": "Manuel Alımlarda", + "OverrideAndAddToDownloadClient": "Geçersiz kıl ve indirme istemcisine ekle", + "OverrideGrabModalTitle": "Geçersiz Kıl ve Al - {title}", + "PrioritySettings": "Öncelik: {priority}", + "IndexerDownloadClientHealthCheckMessage": "Geçersiz indirme istemcilerine sahip indeksleyiciler: {indexerNames}.", + "BuiltIn": "Dahili", + "Script": "Komut Dosyası", + "InfoUrl": "Bilgi URL'si", + "PublishedDate": "Yayınlanma Tarihi", + "Any": "Herhangi", + "AllSearchResultsHiddenByFilter": "Uygulanan filtre nedeniyle tüm arama sonuçları gizlendi.", + "HealthMessagesInfoBox": "Bu sağlık kontrolü mesajlarının nedeni hakkında daha fazla bilgiyi, satırın sonundaki wiki bağlantısına (kitap simgesi) tıklayarak veya [günlüklerinizi]({link}) kontrol ederek bulabilirsiniz. Bu mesajları yorumlamada zorluk çekiyorsanız, aşağıdaki bağlantılardan destek ekibimize ulaşabilirsiniz.", + "PackageVersionInfo": "{packageAuthor} tarafından {packageVersion}", + "LogSizeLimit": "Log Boyutu Sınırı", + "LogSizeLimitHelpText": "Arşivlemeden önce MB cinsinden maksimum log dosya boyutu. Varsayılan 1 MB'tır.", + "AptUpdater": "Güncellemeyi yüklemek için apt'ı kullanın", + "Download": "İndir", + "ErrorRestoringBackup": "Yedekleme geri yüklenirken hata oluştu", + "ExternalUpdater": "{appName} harici bir güncelleme mekanizması kullanacak şekilde yapılandırılmıştır", + "LogFilesLocation": "Log kayıtlarının bulunduğu konum: {location}", + "NoEventsFound": "Etkinlik bulunamadı", + "RestartReloadNote": "Not: {appName} geri yükleme işlemi sırasında otomatik olarak yeniden başlatılacak ve kullanıcı arayüzünü yeniden yükleyecektir.", + "TheLogLevelDefault": "Log seviyesi varsayılan olarak 'Bilgi' şeklindedir ve [Genel Ayarlar](/ayarlar/genel) bölümünden değiştirilebilir", + "UpdateAppDirectlyLoadError": "{appName} doğrudan güncellenemiyor,", + "DockerUpdater": "Güncellemeyi almak için docker konteynerini güncelleyin", + "FailedToFetchUpdates": "Güncellemeler alınamadı", + "Logout": "Çıkış", + "UpdaterLogFiles": "Log Kayıt Güncelleyici", + "WouldYouLikeToRestoreBackup": "'{name}' yedeğini geri yüklemek ister misiniz?", + "InstallLatest": "En Son Sürümü Yükle", + "Install": "Kur", + "InstallMajorVersionUpdate": "Güncellemeyi Kur", + "InstallMajorVersionUpdateMessage": "Bu güncelleştirme yeni bir ana sürüm yükleyecek ve sisteminizle uyumlu olmayabilir. Bu güncelleştirmeyi yüklemek istediğinizden emin misiniz?", + "InstallMajorVersionUpdateMessageLink": "Daha fazla bilgi için lütfen [{domain}]({url}) adresini kontrol edin.", + "Season": "Sezon", + "Artist": "Sanatçı", + "Mixed": "Karışık", + "Stats": "Durum", + "CurrentlyInstalled": "Şuan Kurulu", + "PreviouslyInstalled": "Daha Önce Kurulmuş", + "FailedToFetchSettings": "Ayarlar alınamadı", + "IndexerSettingsCookie": "Çerez", + "IndexerHDBitsSettingsMediumsHelpText": "Belirtilmezse tüm seçenekler kullanılır.", + "IndexerSettingsApiPathHelpText": "API'ye giden yol, genellikle {url}", + "IndexerSettingsAdditionalParameters": "Ek Parametreler", + "IndexerSettingsApiPath": "API Yolu", + "IndexerHDBitsSettingsCodecsHelpText": "Belirtilmezse tüm seçenekler kullanılır.", + "IndexerHDBitsSettingsOriginsHelpText": "Belirtilmezse tüm seçenekler kullanılır.", + "MinimumSeeders": "Minimum Seeder", + "AuthQueries": "Yetkilendirme Talepleri", + "AreYouSureYouWantToDeleteIndexer": "'{name}' uygulamasını {appName} uygulamasından silmek istediğinizden emin misiniz?", + "Auth": "Yetkilendirme", + "AverageQueries": "Talep Ortalaması", + "AverageResponseTimesMs": "Ortalama İndeksleyici Yanıt Süreleri (ms)", + "BookSearch": "Kitap Ara", + "DeleteIndexerProxy": "İndeksleyici Proxy'sini Sil", + "DownloadClientSettingsPriorityItemHelpText": "Öğeleri alırken kullanılacak öncelik", + "EditCategory": "Kategoriyi Düzenle", + "EnableIndexer": "İndeksleyiciyi Etkinleştir", + "IndexerAlphaRatioSettingsExcludeSceneHelpText": "SAHNE sürümlerini sonuçlardan hariç tut", + "IndexerBeyondHDSettingsRefundOnly": "Sadece İade", + "DefaultCategory": "Varsayılan Kategori", + "GrabTitle": "Başlığı Al", + "ApplicationsLoadError": "Uygulama listesi yüklenemiyor", + "IndexerBeyondHDSettingsLimitedOnlyHelpText": "Sadece freeleech'te ara (Sınırlı UL)", + "AreYouSureYouWantToDeleteCategory": "Haritalanan kategoriyi silmek istediğinizden emin misiniz?", + "AverageGrabs": "Ortalama Alım Sayısı", + "DevelopmentSettings": "Geliştirme Ayarları", + "IndexerBeyondHDSettingsRssKeyHelpText": "Siteden RSS Anahtarı (Güvenlik => RSS Anahtarı)", + "ApplicationTagsHelpTextWarning": "Etiketler istenmeyen etkilere neden olabileceğinden dikkatli kullanılmalıdır. Etiketi olan bir uygulama yalnızca aynı etikete sahip indeksleyicilerı eşitler.", + "DeleteApplication": "Uygulamayı Sil", + "IndexerDownloadClientHelpText": "Bu indeksleyiciden {appName} içinde yapılan alımlar için hangi indirme istemcisinin kullanılacağını belirtin", + "DownloadClientSettingsDefaultCategorySubFolderHelpText": "Bir sürüm için eşlenen kategori yoksa varsayılan olarak kullanılacak yedek kategori. {appName}'a özel bir kategori eklemek, {appName} ile ilgisi olmayan indirmelerle çakışmaları önlemeye yardımcı olur. Kategori kullanmak isteğe bağlıdır ancak önemle tavsiye edilir. Çıkış dizininde bir alt dizin [kategori] oluşturur.", + "IncludeManualGrabsHelpText": "{appName} uygulamasında yapılan manuel alımları dahil et", + "IndexerAlphaRatioSettingsExcludeScene": "SAHNE'yi hariç tut", + "AudioSearch": "Müzik Ara", + "IndexerDisabled": "İndeksleyici Devre Dışı", + "ClearHistoryMessageText": "{appName} geçmişinin tamamını temizlemek istediğinizden emin misiniz?", + "ClearHistory": "Geçmişi Temizle", + "ElapsedTime": "Geçen Süre", + "EnabledRedirected": "Etkinleştirildi, Yönlendirildi", + "IndexerAuth": "İndeksleyici Kimlik Doğrulaması", + "EnableRssHelpText": "İndeksleyici için RSS beslemesini etkinleştirin", + "Description": "Tanım", + "AppsMinimumSeeders": "Uygulama İçin Azami Seeder", + "AppsMinimumSeedersHelpText": "Uygulamaların gerektirdiği veri kaynağından alınacak öğelerin minimum seeder sayısı. Boş bırakılırsa varsayılan senkronizasyon profili kullanılır", + "BasicSearch": "Temel Arama", + "DeleteSelectedApplications": "Seçili Uygulamaları Sil", + "DeleteSelectedIndexers": "Seçili İndeksleyicileri Sil", + "IndexerCategories": "İndeksleyici Kategorileri", + "Encoding": "Kodlama", + "FullSync": "Tam Senkronizasyon", + "GoToApplication": "Uygulamaya git", + "BookSearchTypes": "Kitap Arama Türleri", + "Id": "KİMLİK", + "DeleteSelectedIndexer": "Seçili İndeksleyiciyi Sil", + "DeleteAppProfile": "Uygulama Profilini Sil", + "DeleteClientCategory": "İndirme İstemcisi Kategorisini Sil", + "HistoryDetails": "Geçmiş Ayrıntıları", + "IndexerFailureRate": "İndeksleyici Arıza Oranı", + "IndexerSettingsCookieHelpText": "Site Çerezi", + "DisabledUntil": "Şu tarihe kadar devre dışı bırakıldı", + "DownloadClientsSettingsSummary": "{appName} kullanıcı arayüzü aramasına entegrasyon için indirme istemcisi yapılandırması", + "Apps": "Uygulamalar", + "ApplicationTagsHelpText": "Etiketi olmayan veya bir veya daha fazla eşleşen etiketi olan indeksleyicilerı bu uygulamayla senkronize edin. Burada hiçbir etiket listelenmezse, etiketleri nedeniyle hiçbir indeksleyicinun senkronizasyonu engellenmeyecektir.", + "Book": "Kitap", + "DownloadClientCategory": "İstemci Kategorisini İndirin", + "FilterPlaceHolder": "İndeksleyici Ara", + "IndexerAlphaRatioSettingsFreeleechOnlyHelpText": "Yalnızca freeleech sürümlerini arayın", + "IndexerAlreadySetup": "Dizin oluşturucunun en az bir örneği zaten yapılandırılmış", + "IndexerBeyondHDSettingsLimitedOnly": "Sadece Sınırlı", + "ClickToChangeQueryOptions": "Talep seçeneklerini değiştirmek için tıklayın", + "CountIndexersAvailable": "{count} indeksleyici mevcut", + "DownloadClientSettingsDefaultCategoryHelpText": "Bir sürüm için eşlenen kategori yoksa varsayılan olarak kullanılacak yedek kategori. {appName}'a özel bir kategori eklemek, {appName} ile ilgisi olmayan indirmelerle çakışmaları önlemeye yardımcı olur. Kategori kullanmak isteğe bağlıdır ancak önemle tavsiye edilir.", + "IndexerBeyondHDSettingsRewindOnly": "Sadece Geri Sar", + "FoundCountReleases": "{itemCount} sürüm bulundu", + "HistoryCleanup": "Geçmiş Temizliği", + "IndexerSettingsPasskey": "Geçiş Anahtarı", + "IndexerAvistazSettingsPasswordHelpText": "Site Şifresi", + "IndexerAvistazSettingsFreeleechOnlyHelpText": "Yalnızca freeleech sürümlerini arayın", + "IndexerAvistazSettingsUsernameHelpText": "Site Kullanıcı Adı", + "IndexerAvistazSettingsPidHelpText": "Hesabım veya Profilim sayfasının PID'si", + "IndexerAvistazSettingsUsernameHelpTextWarning": "Bu indeksleyicinin API'si yalnızca üyeler ve üzeri kullanıcılar tarafından kullanılabilir.", + "IndexerBeyondHDSettingsApiKeyHelpText": "Site API anahtarı (Güvenlik => API Anahtarı)", + "IndexerBeyondHDSettingsFreeleechOnlyHelpText": "Yalnızca freeleech sürümlerini arayın", + "IndexerBeyondHDSettingsRefundOnlyHelpText": "Sadece iadeyi ara", + "IndexerBeyondHDSettingsRewindOnlyHelpText": "Sadece tekrarları ara", + "IndexerBeyondHDSettingsSearchTypes": "Arama Türleri", + "IndexerBeyondHDSettingsSearchTypesHelpText": "İlginizi çeken sürüm türlerini seçin. Hiçbiri seçilmezse, tüm seçenekler kullanılır.", + "IndexerDetails": "İndeksleyici Ayrıntıları", + "IndexerFileListSettingsFreeleechOnlyHelpText": "Yalnızca freeleech sürümlerini arayın", + "IndexerPassThePopcornSettingsApiUserHelpText": "Bu ayarlar PassThePopcorn güvenlik ayarlarınızda (Profil Düzenle > Güvenlik) bulunur.", + "IndexerProxies": "İndeksleyici Proxy'leri", + "NewznabUrl": "Newznab URL'si", + "Open": "Açık", + "SearchCapabilities": "Arama Yetenekleri", + "TestAllApps": "Tüm Uygulamaları Test Et", + "IndexerVipExpiringHealthCheckMessage": "İndeksleyici VIP avantajlarının süresi yakında doluyor: {indexerNames}", + "IndexerVipExpiredHealthCheckMessage": "İndeksleyici VIP avantajları sona erdi: {indexerNames}", + "IndexerGazelleGamesSettingsSearchGroupNames": "Grup Adlarını Ara", + "IndexerSettingsGrabLimit": "Alım Sınırı", + "PreferMagnetUrl": "Magnet URL'sini tercih edin", + "PreferMagnetUrlHelpText": "Etkinleştirildiğinde, bu indeksleyici torrent bağlantılarına geri dönüş için magnet URL'lerinin kullanımını tercih edecektir", + "Redirected": "Yönlendirildi", + "SearchCountIndexers": "{count} indeksleyiciyi ara", + "SearchType": "Arama Türü", + "SyncAppIndexers": "İndeksleyicileri Senkronize Et", + "TVSearchTypes": "TV Arama Türleri", + "TotalIndexerQueries": "Toplam İndeksleyici Sorguları", + "TotalUserAgentQueries": "Kullanıcı Aracısı Başına Toplam Sorgu Sayısı", + "LastFailure": "Son Hata", + "RssQueries": "RSS Sorguları", + "ProxyValidationUnableToConnect": "Proxy'ye bağlanılamıyor: {exceptionMessage}. Ayrıntılar için bu hatayla ilgili günlüğü kontrol edin", + "QueryResults": "Sorgu Sonuçları", + "IndexerFileListSettingsUsernameHelpText": "Site Kullanıcı Adı", + "IndexerGazelleGamesSettingsFreeleechOnlyHelpText": "Yalnızca freeleech sürümlerini arayın", + "IndexerHDBitsSettingsUseFilenamesHelpText": "Torrent dosya adlarını sürüm başlıkları olarak kullanmak istiyorsanız bu seçeneği işaretleyin", + "IndexerHDBitsSettingsUsernameHelpText": "Site Kullanıcı Adı", + "IndexerHistoryLoadError": "İndeksleyici geçmişi yüklenirken hata oluştu", + "IndexerIPTorrentsSettingsCookieUserAgent": "Çerezler için Kullanıcı Aracısı", + "IndexerId": "İndeksleyici Kimliği", + "IndexerMTeamTpSettingsFreeleechOnlyHelpText": "Yalnızca freeleech sürümlerini arayın", + "IndexerNebulanceSettingsApiKeyHelpText": "API Anahtarı Kullanıcı Ayarları > Api Anahtarları'ndan. Anahtarın Listeleme ve İndirme izinlerine sahip olması gerekir", + "IndexerNewznabSettingsVipExpirationHelpText": "VIP Son Kullanma Tarihi için (yyyy-aa-gg) tarihini girin veya boş bırakın, {appName} VIP'nin sona ermesinden 1 hafta sonra bildirimde bulunacaktır", + "IndexerSettingsApiUser": "API Kullanıcısı", + "IndexerSettingsBaseUrl": "Temel URL", + "IndexerSettingsBaseUrlHelpText": "{appName}'ın siteye yönelik istekler için hangi temel URL'yi kullanacağını seçin", + "IndexerSettingsLimitsUnit": "Limit Birimi", + "IndexerSettingsLimitsUnitHelpText": "İndeksleyici başına limitleri saymak için zaman birimi", + "IndexerSettingsRssKey": "RSS Anahtarı", + "MinimumSeedersHelpText": "İndeksleyicinin alım yapması için uygulamanın gerektirdiği minimum seeder sayısı", + "MusicSearchTypes": "Müzik Arama Türleri", + "NoApplicationsFound": "Hiçbir uygulama bulunamadı", + "OnGrabHelpText": "Yayın Alındığında", + "PackSeedTime": "Paket Seed Süresi", + "ProwlarrDownloadClientsAlert": "{appName} içinde doğrudan arama yapmayı düşünüyorsanız, İndirme İstemcileri eklemeniz gerekir. Aksi takdirde, bunları buraya eklemeniz gerekmez. Uygulamalarınızdan yapılan aramalar için, bunun yerine orada yapılandırılan indirme istemcileri kullanılır.", + "UnableToLoadAppProfiles": "Uygulama profilleri yüklenemiyor", + "UnableToLoadDevelopmentSettings": "Geliştirme ayarları yüklenemiyor", + "IndexerHDBitsSettingsOrigins": "Kaynaklar", + "IndexerNoDefinitionCheckHealthCheckMessage": "{indexerNames} indeksleyicileri tanımsız olduğundan çalışmayacaktır. Lütfen bunları kaldırın ve/veya {appName} uygulamasına yeniden ekleyin.", + "MovieSearchTypes": "Film Arama Türleri", + "NoIndexerCategories": "Bu indeksleyici için hiçbir kategori bulunamadı", + "PackSeedTimeHelpText": "Bir paketin (sezon veya diskografi) torrentinin durdurulmadan önce aktif olması gereken süre, boş bırakılırsa uygulamanın varsayılan değeri kullanılır", + "IndexerSettingsGrabLimitHelpText": "{appName}'ın siteye izin vereceği ilgili birim tarafından belirtilen maksimum kapma sayısı", + "IndexerSettingsPreferMagnetUrlHelpText": "Etkinleştirildiğinde, bu indeksleyici torrent bağlantılarına geri dönüş için magnet URL'lerinin kullanımını tercih edecektir", + "Private": "Özel", + "ProwlarrSupportsAnyIndexer": "{appName}, 'Generic Newznab' (usenet için) veya 'Generic Torznab' (torrentler için) kullanan Newznab/Torznab standardını kullanan herhangi bir indeksleyiciye ek olarak birçok indeksleyiciyi destekler. Aşağıdan indeksleyicinizi arayın ve seçin.", + "IndexerInfo": "İndeksleyici Bilgileri", + "IndexerMTeamTpSettingsApiKeyHelpText": "Siteden API Anahtarı (Kullanıcı Kontrol Paneli => Güvenlik => Laboratuvar'da Bulunur)", + "IndexerPassThePopcornSettingsFreeleechOnlyHelpText": "Yalnızca freeleech sürümlerini arayın", + "IndexerRedactedSettingsApiKeyHelpText": "Siteden API Anahtarı (Ayarlar => Erişim Ayarları'nda Bulunur)", + "IndexerSettingsQueryLimitHelpText": "{appName}'in siteye izin vereceği ilgili birim tarafından belirtilen maksimum sorgu sayısı", + "IndexerSettingsVipExpiration": "VIP Son Kullanma Tarihi", + "Parameters": "Parametreler", + "TotalQueries": "Toplam Sorgular", + "SettingsIndexerLogging": "Gelişmiş İndeksleyici Günlüğü", + "SettingsLogRotate": "Günlük Döndürme", + "TotalIndexerSuccessfulGrabs": "İndeksleyicinin Toplam Başarılı Alma Sayısı", + "SyncProfiles": "Profilleri Senkronize Et", + "IndexerProxy": "İndeksleyici Proxy", + "Redirect": "Yönlendir", + "SettingsSqlLoggingHelpText": "{appName} uygulamasından gelen tüm SQL sorgularını günlüğe kaydet", + "Proxies": "Proxy'ler", + "Public": "Herkese Açık", + "RepeatSearch": "Tekrar Ara", + "ManageApplications": "Uygulamaları Yönet", + "SearchAllIndexers": "Tüm indeksleyicilerde ara", + "SeedRatioHelpText": "Bir torrentin durdurulmadan önce ulaşması gereken oran, boş bırakıldığında uygulamanın varsayılanı kullanılır", + "SeedTimeHelpText": "Bir torrentin durdurulmadan önce aktif olması gereken süre, boş bırakıldığında uygulamanın varsayılanı kullanılır", + "IndexerRss": "İndeksleyici RSS", + "SearchTypes": "Arama Türleri", + "SemiPrivate": "Yarı Özel", + "SettingsLogSql": "Sql Günlüğü", + "SelectedCountOfCountReleases": "{itemCount} sürümden {selectedCount} tanesi seçildi", + "IndexerSettingsSummary": "Proxy'ler de dahil olmak üzere çeşitli genel indeksleyici ayarlarını yapılandırın.", + "SettingsLogRotateHelpText": "Günlük klasöründe saklanacak maksimum günlük dosyası sayısı", + "TvSearch": "Dizi Ara", + "SelectIndexers": "İndeksleyici Seç", + "Privacy": "Gizlilik", + "SettingsConsoleLogLevel": "Konsol Günlük Düzeyi", + "SettingsFilterSentryEventsHelpText": "Bilinen kullanıcı hatası olaylarının Analitik olarak gönderilmesini filtreleyin", + "SyncLevel": "Senkronizasyon Seviyesi", + "IndexerName": "İndeksleyici Adı", + "IndexerSite": "İndeksleyici Sitesi", + "IndexerTagsHelpTextWarning": "Etiketler dikkatli kullanılmalıdır, istenmeyen etkilere neden olabilirler. Etiketli bir indeksleyici yalnızca aynı etikete sahip uygulamalarla senkronize olur.", + "InitialFailure": "İlk Hata", + "MappedCategories": "Haritalanmış Kategoriler", + "NotSupported": "Desteklenmiyor", + "SettingsIndexerLoggingHelpText": "Yanıt dahil olmak üzere ek İndeksleyici verilerini günlüğe kaydet", + "SearchQueries": "Arama Sorguları", + "SyncProfile": "Profil Senkronizasyonu", + "NoSearchResultsFound": "Hiçbir arama sonucu bulunamadı, aşağıdan yeni bir arama yapmayı deneyin.", + "RssFeed": "RSS Beslemesi", + "VipExpiration": "VIP Son Kullanma Tarihi", + "IndexerPassThePopcornSettingsApiKeyHelpText": "Site API Anahtarı", + "IndexerSettingsFreeleechOnly": "Sadece Freeleech", + "NoIndexerHistory": "Bu indeksleyici için geçmiş bulunamadı", + "SearchIndexers": "İndeksleyicilerde Ara", + "Url": "Url", + "Website": "Web site", + "IndexerTagsHelpText": "Etiketleri kullanarak İndeksleyici Proxy'lerini veya İndeksleyici'nin hangi uygulamalarla senkronize edileceğini belirtin.", + "MassEditor": "Kitle Editörü", + "RawSearchSupported": "Ham Arama Destekleniyor", + "SettingsFilterSentryEvents": "Analitik Olayları Filtrele", + "TorznabUrl": "Torznab Url", + "TotalHostGrabs": "İstemci Başına Toplam Alım", + "TotalUserAgentGrabs": "Kullanıcı Aracısı Başına Toplam Alım Sayısı", + "IndexerGazelleGamesSettingsApiKeyHelpText": "Siteden API Anahtarı (Ayarlar => Erişim Ayarları'nda Bulunur)", + "IndexerObsoleteCheckMessage": "İndeksleyiciler eski veya güncellendi: {0}. Lütfen {appName}'i kaldırın ve (veya) yeniden ekleyin", + "IndexerNzbIndexSettingsApiKeyHelpText": "Site API Anahtarı", + "IndexerOrpheusSettingsApiKeyHelpText": "Siteden API Anahtarı (Ayarlar => Erişim Ayarları'nda Bulunur)", + "IndexerHDBitsSettingsPasskeyHelpText": "Kullanıcı Detaylarından Geçiş Anahtarı", + "IndexerQuery": "İndeksleyici Sorgusu", + "MovieSearch": "Film Arama", + "TotalGrabs": "Toplam Alınan", + "TotalHostQueries": "İstemci Başına Toplam Sorgu", + "Query": "Sorgu", + "QueryOptions": "Sorgu Seçenekleri", + "SyncLevelAddRemove": "Yalnızca Ekle ve Kaldır: {appName} uygulamasından indeksleyiciler eklendiğinde veya kaldırıldığında, bu uzak uygulama güncellenecektir.", + "IndexerIPTorrentsSettingsCookieUserAgentHelpText": "Tarayıcıdan kullanılan çerezle ilişkili Kullanıcı Aracısı", + "IndexerIPTorrentsSettingsFreeleechOnlyHelpText": "Yalnızca freeleech sürümlerini arayın", + "IndexerSettingsAppsMinimumSeedersHelpText": "Uygulamalar tarafından indeksleyicinin alım yapması için gereken minimum sedeerlar, boş bırakılırsa Eşitleme profilinin varsayılanı kullanılacaktır", + "IndexerSettingsQueryLimit": "Sorgu Limiti", + "IndexerHDBitsSettingsFreeleechOnlyHelpText": "Yalnızca freeleech sürümlerini göster", + "IndexerHDBitsSettingsUseFilenames": "Dosya Adlarını Kullan", + "IndexerNewznabSettingsAdditionalParametersHelpText": "Ek Newznab parametreleri", + "IndexerNewznabSettingsApiKeyHelpText": "Site API Anahtarı", + "IndexerSettingsAppsMinimumSeeders": "Uygulamalar Minimum Seeders", + "RedirectHelpText": "İndeksleyici için gelen indirme isteğini yeniden yönlendirin ve isteği {appName} aracılığıyla proxy olarak göndermek yerine doğrudan alım yapmayı tercih edin", + "SyncLevelFull": "Tam Senkronizasyon: Bu uygulamanın indeksleyicilerini tamamen senkronize halde tutar. {appName} içindeki indeksleyicilerde yapılan değişiklikler daha sonra bu uygulamayla senkronize edilir. Bu uygulama içinde indeksleyicilerde uzaktan yapılan herhangi bir değişiklik, bir sonraki senkronizasyonda {appName} tarafından geçersiz kılınır.", + "IndexerSettingsPreferMagnetUrl": "Magnet URL'sini Tercih Et", + "IndexerTorrentSyndikatSettingsApiKeyHelpText": "Site API Anahtarı", + "ProwlarrDownloadClientsInAppOnlyAlert": "İndirme istemcileri yalnızca {appName} uygulama içi aramalar içindir ve uygulamalarla senkronize edilmez. Geliştiricilerin bu tür bir işlevsellik ekleme planı yoktur.", + "ProwlarrSupportsAnyDownloadClient": "{appName} aşağıda listelenen indirme istemcilerini destekler.", + "UnableToLoadIndexerProxies": "İndeksleyici Proxy'leri yüklenemiyor", + "IndexerStatus": "İndeksleyici Durumu", + "IndexerPassThePopcornSettingsGoldenPopcornOnly": "Sadece Golden Popcorn", + "IndexerPassThePopcornSettingsGoldenPopcornOnlyHelpText": "Yalnızca Golden Popcorn sürümlerini arayın", + "IndexerSettingsPackSeedTime": "Paket Seed Süresi", + "IndexerSettingsPackSeedTimeIndexerHelpText": "Bir paketin (sezon veya diskografi) torrentinin durdurulmadan önce edilmesi gereken süre, boş bırakılırsa varsayılan değeri kullanılır", + "IndexerFileListSettingsPasskeyHelpText": "Site Parolası (Bu, indirme istemcinizde gösterilen izleyici URL'sindeki alfanümerik dizedir)", + "IndexerGazelleGamesSettingsApiKeyHelpTextWarning": "Kullanıcı ve Torrent izinlerine sahip olmalısınız", + "IndexerGazelleGamesSettingsSearchGroupNamesHelpText": "Grup adlarına göre sürüm ara", + "IndexerHealthCheckNoIndexers": "Hiçbir indeksleyici etkinleştirilmedi, {appName} arama sonuçlarını döndürmeyecek", + "QueryType": "Sorgu Türü", + "DownloadClientUTorrentProviderMessage": "uTorrent'in kripto para madenciliği, kötü amaçlı yazılım ve reklam içerme geçmişi vardır, bu nedenle farklı bir istemci seçmenizi önemle tavsiye ederiz." } diff --git a/src/NzbDrone.Core/Localization/Core/uk.json b/src/NzbDrone.Core/Localization/Core/uk.json index f8239e860..f42bd890b 100644 --- a/src/NzbDrone.Core/Localization/Core/uk.json +++ b/src/NzbDrone.Core/Localization/Core/uk.json @@ -181,7 +181,7 @@ "UILanguageHelpTextWarning": "Потрібно перезавантажити браузер", "UnableToAddANewIndexerPleaseTryAgain": "Не вдалося додати новий індексатор, спробуйте ще раз.", "UnableToAddANewNotificationPleaseTryAgain": "Не вдалося додати нове сповіщення, спробуйте ще раз.", - "UnableToLoadBackups": "Не вдалося завантажити резервні копії", + "BackupsLoadError": "Не вдалося завантажити резервні копії", "UnableToLoadUISettings": "Не вдалося завантажити налаштування інтерфейсу користувача", "UnsavedChanges": "Незбережені зміни", "UnselectAll": "Скасувати вибір усіх", @@ -250,7 +250,7 @@ "Enabled": "Увімкнено", "EnableInteractiveSearchHelpText": "Буде використано, коли використовується інтерактивний пошук", "Indexers": "Індексатори", - "HealthNoIssues": "Немає проблем із вашою конфігурацією", + "NoIssuesWithYourConfiguration": "Немає проблем із вашою конфігурацією", "IndexerFlags": "Прапори індексатора", "IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Усі індексатори недоступні через збої більше 6 годин", "IndexerLongTermStatusUnavailableHealthCheckMessage": "Індексатори недоступні через збої більше 6 годин: {indexerNames}", @@ -292,7 +292,7 @@ "Style": "Стиль", "SuggestTranslationChange": "Запропонуйте зміну перекладу", "TableOptionsColumnsMessage": "Виберіть, які стовпці відображаються та в якому порядку вони відображаються", - "SystemTimeCheckMessage": "Системний час вимкнено більш ніж на 1 день. Заплановані завдання можуть не працювати належним чином, доки час не буде виправлено", + "SystemTimeHealthCheckMessage": "Системний час вимкнено більш ніж на 1 день. Заплановані завдання можуть не працювати належним чином, доки час не буде виправлено", "OnGrab": "При захопленні", "SSLCertPath": "Шлях сертифіката SSL", "UI": "Інтерфейс користувача", @@ -336,7 +336,7 @@ "ApplicationLongTermStatusCheckSingleClientMessage": "Індексатори недоступні через збої більше 6 годин: {0}", "Remove": "Видалити", "Replace": "Замінити", - "TheLatestVersionIsAlreadyInstalled": "Остання версія {appName} вже встановлена", + "OnLatestVersion": "Остання версія {appName} вже встановлена", "ApplicationURL": "URL програми", "Theme": "Тема", "ApplyTagsHelpTextAdd": "Додати: додати теги до наявного списку тегів", @@ -410,5 +410,44 @@ "DownloadClientQbittorrentSettingsUseSslHelpText": "Використовувати безпечне з'єднання. Дивіться параметри -> Web UI -> 'Use HTTPS instead of HTTP' в qBittorrent.", "DownloadClientSettingsInitialStateHelpText": "Початковий стан для торрентів, доданих до {clientName}", "AuthenticationRequiredPasswordConfirmationHelpTextWarning": "Підтвердити новий пароль", - "DownloadClientAriaSettingsDirectoryHelpText": "Додаткове розташування для розміщення завантажень. Залиште поле порожнім, щоб використовувати стандартне розташування Aria2" + "DownloadClientAriaSettingsDirectoryHelpText": "Додаткове розташування для розміщення завантажень. Залиште поле порожнім, щоб використовувати стандартне розташування Aria2", + "ApiKeyValidationHealthCheckMessage": "Будь ласка оновіть ключ API, щоб він містив принаймні {length} символів. Ви можете зробити це в налаштуваннях або в файлі конфігурації", + "DownloadClientRTorrentSettingsDirectoryHelpText": "Додаткове розташування для розміщення завантажень. Залиште поле порожнім, щоб використовувати стандартне розташування Aria2", + "IndexerHDBitsSettingsCodecs": "Кодек", + "DownloadClientSettingsUrlBaseHelpText": "Додає префікс до URL-адреси {connectionName}, наприклад {url}", + "DownloadClientTransmissionSettingsDirectoryHelpText": "Додаткове розташування для розміщення завантажень. Залиште поле порожнім, щоб використовувати стандартне розташування Aria2", + "ProxyValidationBadRequest": "Не вдалося перевірити проксі. Код стану: {statusCode}", + "CustomFilter": "Користувацькі фільтри", + "IndexerHDBitsSettingsMediums": "Середній", + "Default": "За замовчуванням", + "GrabRelease": "Захопити реліз", + "Clone": "Клонування", + "CountDownloadClientsSelected": "Вибрано {count} клієнтів завантажувача", + "Script": "Сценарій", + "Any": "Будь-який", + "BuiltIn": "Вбудований", + "PublishedDate": "Дата публікації", + "AllSearchResultsHiddenByFilter": "Всі результати приховані фільтром", + "AptUpdater": "Використовуйте apt для інсталяції оновлення", + "DockerUpdater": "Оновіть контейнер docker, щоб отримати оновлення", + "UpdateAppDirectlyLoadError": "Неможливо оновити {appName} безпосередньо,", + "Download": "Завантажити", + "ErrorRestoringBackup": "Помилка відновлення резервної копії", + "ExternalUpdater": "{appName} налаштовано на використання зовнішнього механізму оновлення", + "NoEventsFound": "Подій не знайдено", + "RestartReloadNote": "Примітка: {appName} автоматично перезапуститься та перезавантажить інтерфейс під час процесу відновлення.", + "InstallLatest": "Встановити останній", + "Mixed": "Виправлено", + "CurrentlyInstalled": "В даний час встановлено", + "Season": "Причина", + "Stats": "Статус", + "CountIndexersSelected": "{count} індексер(-и) обрано", + "SeedRatio": "Коефіцієнт роздачі", + "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "Якщо торрент заблоковано хешем, він може не бути належним чином відхилений під час RSS/пошуку для деяких індексаторів. Увімкнення цього параметра дозволить відхилити його після захоплення торента, але до його відправки клієнту.", + "MinimumSeeders": "Мінімум сидерів (роздаючих)", + "SeedTime": "Час сидіння", + "Author": "Автор", + "OnHealthRestoredHelpText": "При відновленні стану", + "IndexerHDBitsSettingsOriginsHelpText": "Якщо не вказано, використовуються всі параметри.", + "days": "дні(в)" } diff --git a/src/NzbDrone.Core/Localization/Core/vi.json b/src/NzbDrone.Core/Localization/Core/vi.json index 294d7cbe3..704eb38df 100644 --- a/src/NzbDrone.Core/Localization/Core/vi.json +++ b/src/NzbDrone.Core/Localization/Core/vi.json @@ -60,7 +60,7 @@ "SSLPort": "Cổng SSL", "Style": "Phong cách", "SuggestTranslationChange": "Đề xuất thay đổi bản dịch", - "SystemTimeCheckMessage": "Thời gian hệ thống tắt hơn 1 ngày. Các tác vụ đã lên lịch có thể không chạy chính xác cho đến khi thời gian được sửa", + "SystemTimeHealthCheckMessage": "Thời gian hệ thống tắt hơn 1 ngày. Các tác vụ đã lên lịch có thể không chạy chính xác cho đến khi thời gian được sửa", "TableOptions": "Tùy chọn bảng", "TableOptionsColumnsMessage": "Chọn cột nào hiển thị và chúng xuất hiện theo thứ tự nào", "TagCannotBeDeletedWhileInUse": "Không thể bị xóa khi đang sử dụng", @@ -214,7 +214,7 @@ "UnableToAddANewDownloadClientPleaseTryAgain": "Không thể thêm ứng dụng khách tải xuống mới, vui lòng thử lại.", "UnableToAddANewIndexerProxyPleaseTryAgain": "Không thể thêm trình chỉ mục mới, vui lòng thử lại.", "UnableToAddANewNotificationPleaseTryAgain": "Không thể thêm thông báo mới, vui lòng thử lại.", - "UnableToLoadBackups": "Không thể tải các bản sao lưu", + "BackupsLoadError": "Không thể tải các bản sao lưu", "DownloadClientsLoadError": "Không thể tải ứng dụng khách tải xuống", "UnableToLoadGeneralSettings": "Không thể tải Cài đặt chung", "UnableToLoadHistory": "Không thể tải lịch sử", @@ -256,7 +256,7 @@ "Grabbed": "Nắm lấy", "Grabs": "Vồ lấy", "Health": "Sức khỏe", - "HealthNoIssues": "Không có vấn đề với cấu hình của bạn", + "NoIssuesWithYourConfiguration": "Không có vấn đề với cấu hình của bạn", "RestartRequiredHelpTextWarning": "Yêu cầu khởi động lại để có hiệu lực", "ShowSearch": "Hiển thị Tìm kiếm", "ShowSearchHelpText": "Hiển thị nút tìm kiếm khi di chuột", @@ -329,7 +329,7 @@ "Queued": "Đã xếp hàng", "Remove": "Tẩy", "Replace": "Thay thế", - "TheLatestVersionIsAlreadyInstalled": "Phiên bản mới nhất của {appName} đã được cài đặt", + "OnLatestVersion": "Phiên bản mới nhất của {appName} đã được cài đặt", "ApplyChanges": "Áp dụng thay đổi", "ApplyTagsHelpTextAdd": "Thêm: Thêm thẻ vào danh sách thẻ hiện có", "ApplyTagsHelpTextHowToApplyApplications": "Cách áp dụng thẻ cho các phim đã chọn", @@ -355,5 +355,47 @@ "None": "không ai", "ResetAPIKeyMessageText": "Bạn có chắc chắn muốn đặt lại Khóa API của mình không?", "ApiKeyValidationHealthCheckMessage": "Hãy cập nhật mã API để dài ít nhất {length} kí tự. Bạn có thể làm điều này trong cài đặt hoặc trong tập config", - "RestartProwlarr": "Khởi động lại {appName}" + "RestartProwlarr": "Khởi động lại {appName}", + "CustomFilter": "Bộ lọc tùy chỉnh", + "IndexerHDBitsSettingsMediums": "Trung bình", + "ProxyValidationBadRequest": "Không thể kiểm tra proxy. Mã trạng thái: {statusCode}", + "GrabRelease": "Lấy bản phát hành", + "BuiltIn": "Được xây dựng trong", + "Script": "Kịch bản", + "PublishedDate": "Ngày xuất bản", + "AllSearchResultsHiddenByFilter": "Tất cả kết quả bị ẩn bởi bộ lọc được áp dụng", + "AptUpdater": "Sử dụng apt để cài đặt bản cập nhật", + "DockerUpdater": "cập nhật vùng chứa docker để nhận bản cập nhật", + "Download": "Tải xuống", + "NoEventsFound": "Không tìm thấy sự kiện", + "ErrorRestoringBackup": "Lỗi khi khôi phục bản sao lưu", + "ExternalUpdater": "{appName} được định cấu hình để sử dụng cơ chế cập nhật bên ngoài", + "RestartReloadNote": "Lưu ý: {appName} sẽ tự động khởi động lại và tải lại giao diện người dùng trong quá trình khôi phục.", + "UpdateAppDirectlyLoadError": "Không thể cập nhật {appName} trực tiếp,", + "InstallLatest": "Cài đặt mới nhất", + "AuthenticationMethodHelpTextWarning": "Vui lòng chọn một phương thức xác thực hợp lệ", + "AuthenticationRequiredPasswordHelpTextWarning": "Nhập mật khẩu mới", + "AuthenticationRequiredUsernameHelpTextWarning": "Nhập tên người dùng mới", + "UpdaterLogFiles": "Tệp nhật ký của trình cập nhật", + "UseSsl": "Dùng SSL", + "AppUpdatedVersion": "{appName} đã được cập nhật lên phiên bản `{version}`, để nhận được những thay đổi mới nhất, bạn cần tải lại {appName}", + "AuthenticationRequiredPasswordConfirmationHelpTextWarning": "Xác nhận mật khẩu mới", + "UpdateAvailableHealthCheckMessage": "Có cập nhật mới: {version}", + "AuthenticationRequiredWarning": "Để ngăn truy cập từ xa mà không cần xác thực, {appName} hiện yêu cầu bật xác thực. Bạn có thể tùy ý tắt xác thực từ các địa chỉ cục bộ.", + "AuthenticationRequired": "Bắt buộc phải xác thực", + "AuthenticationMethod": "Phương thức xác thực", + "AddDownloadClientImplementation": "Thêm trình tải xuống - {implementationName}", + "AddIndexerImplementation": "Thêm trình lập chỉ mục - {implementationName}", + "AddConnection": "Thêm kết nối", + "AddConnectionImplementation": "Thêm điều kiện - {implementationName}", + "AppUpdated": "{appName} đã cập nhật", + "Clone": "Đóng", + "EditIndexerImplementation": "Thêm điều kiện - {implementationName}", + "CurrentlyInstalled": "Mới cài đặt", + "EditApplicationImplementation": "Thêm điều kiện - {implementationName}", + "Stats": "Trạng thái", + "Season": "Lý do", + "Mixed": "đã sửa", + "EditConnectionImplementation": "Thêm điều kiện - {implementationName}", + "AddApplicationImplementation": "Thêm điều kiện - {implementationName}" } diff --git a/src/NzbDrone.Core/Localization/Core/zh_CN.json b/src/NzbDrone.Core/Localization/Core/zh_CN.json index af96c24de..41f815655 100644 --- a/src/NzbDrone.Core/Localization/Core/zh_CN.json +++ b/src/NzbDrone.Core/Localization/Core/zh_CN.json @@ -1,6 +1,6 @@ { "About": "关于", - "AcceptConfirmationModal": "接受确认模组Accept Confirmation Modal", + "AcceptConfirmationModal": "接受确认对话框", "Actions": "动作", "Add": "添加", "AddApplication": "添加应用程序", @@ -13,18 +13,18 @@ "AddRemoveOnly": "仅添加和删除", "AddSyncProfile": "添加同步配置文件", "AddToDownloadClient": "添加发布到下载客户端", - "Added": "已添加", + "Added": "添加日期", "AddedToDownloadClient": "发布已添加档案到客户端", "AddingTag": "添加标签", - "Age": "年龄", + "Age": "寿命", "All": "全部", "AllIndexersHiddenDueToFilter": "由于应用了筛选器,所有索引器都被隐藏。", "Analytics": "分析", - "AnalyticsEnabledHelpText": "将匿名使用情况和错误信息发送到{appName}的服务器。这包括有关您的浏览器的信息、您使用的{appName} WebUI页面、错误报告以及操作系统和运行时版本。我们将使用此信息来确定功能和错误修复的优先级。", - "ApiKey": "接口密钥 (API Key)", - "ApiKeyValidationHealthCheckMessage": "请将API密钥更新为至少{length}个字符长。您可以通过设置或配置文件执行此操作", - "AppDataDirectory": "AppData目录", - "AppDataLocationHealthCheckMessage": "正在更新期间的 AppData 不会被更新删除", + "AnalyticsEnabledHelpText": "将匿名使用情况和错误信息发送到 {appName} 的服务器。这包括有关您的浏览器信息、您使用的 {appName} WebUI页面、错误报告以及操作系统和运行时版本。我们将使用此信息来确定功能和错误修复的优先级。", + "ApiKey": "API 密钥", + "ApiKeyValidationHealthCheckMessage": "请将API密钥更新为至少 {length} 个字符长。您可以通过设置或配置文件完成此操作", + "AppDataDirectory": "AppData 目录", + "AppDataLocationHealthCheckMessage": "为防止在更新时删除 AppData,更新将无法进行", "AppProfileInUse": "正在使用的应用程序配置文件", "AppProfileSelectHelpText": "应用程序配置用于控制应用程序同步设置 RSS、自动搜索和交互式搜索设置", "AppSettingsSummary": "配置{appName}与PVR程序交互方式的应用和设置", @@ -34,7 +34,7 @@ "ApplicationStatusCheckAllClientMessage": "由于故障所用应用程序都不可用", "ApplicationStatusCheckSingleClientMessage": "由于故障应用程序不可用", "ApplicationURL": "应用程序 URL", - "ApplicationUrlHelpText": "此应用的外部URL,包含 http(s)://、端口和基本URL", + "ApplicationUrlHelpText": "此应用的外部 URL,包含 http(s)://、端口和基本 URL", "Applications": "程序", "Apply": "应用", "ApplyTags": "应用标签", @@ -45,9 +45,9 @@ "Auth": "认证", "Authentication": "认证", "AuthenticationMethodHelpText": "需要用户名和密码以访问 {appName}", - "AuthenticationRequired": "需要身份验证", - "AuthenticationRequiredHelpText": "修改哪些请求需要认证。除非你了解其中的风险,否则不要更改。", - "AuthenticationRequiredWarning": "为了防止未经身份验证的远程访问,{appName} 现在需要启用身份验证。您可以禁用本地地址的身份验证。", + "AuthenticationRequired": "需要认证", + "AuthenticationRequiredHelpText": "修改这些请求需要认证。除非你了解其中的风险,否则不要进行更改。", + "AuthenticationRequiredWarning": "为防止未经认证的远程访问,{appName} 现需要启用身份认证。您可以选择禁用本地地址的身份认证。", "Author": "作者", "Automatic": "自动化", "AutomaticSearch": "自动搜索", @@ -89,7 +89,7 @@ "ConnectSettingsSummary": "通知和自定义脚本", "ConnectionLost": "连接丢失", "Connections": "连接", - "CouldNotConnectSignalR": "无法连接至SignalR,不会升级UI", + "CouldNotConnectSignalR": "无法连接至 SignalR,UI 将不会更新", "Custom": "自定义", "CustomFilters": "自定义过滤器", "DatabaseMigration": "数据库迁移版本", @@ -101,14 +101,14 @@ "DeleteApplication": "删除应用程序", "DeleteApplicationMessageText": "您确定要删除应用程序“{name}”吗?", "DeleteBackup": "删除备份", - "DeleteBackupMessageText": "您确定要删除备份“{name}”吗?", + "DeleteBackupMessageText": "您确定要删除备份 “{name}” 吗?", "DeleteClientCategory": "删除下载客户端分类", "DeleteDownloadClient": "删除下载客户端", - "DeleteDownloadClientMessageText": "你确定要删除下载客户端 “{name}” 吗?", + "DeleteDownloadClientMessageText": "您确定要删除下载客户端 “{name}” 吗?", "DeleteIndexerProxy": "删除搜刮器代理", "DeleteIndexerProxyMessageText": "您确定要删除索引器代理“{name}”吗?", "DeleteNotification": "删除消息推送", - "DeleteNotificationMessageText": "您确定要删除通知“{name}”吗?", + "DeleteNotificationMessageText": "您确定要删除通知 “{name}” 吗?", "DeleteTag": "删除标签", "DeleteTagMessageText": "您确定要删除标签 '{label}' 吗?", "Description": "描述", @@ -116,7 +116,7 @@ "DevelopmentSettings": "开发设置", "Disabled": "禁用", "DisabledUntil": "禁用Until", - "Discord": "分歧", + "Discord": "Discord", "Docker": "Docker", "Donations": "赞助", "DownloadClient": "下载客户端", @@ -133,7 +133,7 @@ "ElapsedTime": "运行时间", "Enable": "启用", "EnableAutomaticSearch": "启用自动搜索", - "EnableAutomaticSearchHelpText": "当自动搜索通过UI或{appName}执行时将被使用", + "EnableAutomaticSearchHelpText": "当自动搜索通过 UI 或 {appName} 执行时将被使用", "EnableIndexer": "启用搜刮器", "EnableInteractiveSearch": "启用手动搜索", "EnableInteractiveSearchHelpText": "当手动搜索启用时使用", @@ -173,7 +173,7 @@ "Grabbed": "已抓取", "Grabs": "抓取", "Health": "健康度", - "HealthNoIssues": "您的设置没有问题", + "NoIssuesWithYourConfiguration": "您的设置没有问题", "HideAdvanced": "隐藏高级设置", "History": "历史记录", "HistoryCleanup": "清理历史记录", @@ -194,13 +194,13 @@ "IndexerDetails": "‎索引器‎‎详细信息‎", "IndexerDisabled": "索引器已被禁用", "IndexerFailureRate": "Indexer失败率", - "IndexerFlags": "搜刮器标记", + "IndexerFlags": "索引器标志", "IndexerHealthCheckNoIndexers": "未启用任何搜刮器,{appName}将不会返回搜索结果", "IndexerInfo": "索引器信息", "IndexerLongTermStatusAllUnavailableHealthCheckMessage": "由于故障超过6小时,所有搜刮器均不可用", "IndexerLongTermStatusUnavailableHealthCheckMessage": "由于故障6小时,下列搜刮器都已不可用:{indexerNames}", "IndexerName": "‎索引‎‎名字‎", - "IndexerNoDefCheckMessage": "索引器没有定义,将无法工作: {0}. 请删除或重新添加到{appName}", + "IndexerNoDefinitionCheckHealthCheckMessage": "索引器没有定义,将无法工作: {indexerNames}. 请删除或重新添加到{appName}", "IndexerObsoleteCheckMessage": "搜刮器已过弃用或已更新:{0}。请将其删除和(或)重新添加到 {appName}", "IndexerPriority": "搜刮器优先级", "IndexerPriorityHelpText": "索引器优先级从1(最高)到50(最低),默认25。", @@ -307,7 +307,7 @@ "ProwlarrSupportsAnyIndexer": "{appName}支持多种搜刮器,包括任何使用Newznab/Torznab标准的搜刮器(“通用Newznab”对应Usenet,“Generic Torznab”对应Torrents)。从以下搜索并选择你的搜刮器。", "Proxies": "代理", "Proxy": "代理", - "ProxyBypassFilterHelpText": "使用“ , ”作为分隔符,和“ *. ”作为二级域名的通配符", + "ProxyBypassFilterHelpText": "使用 “ , ” 作为分隔符,并使用 “ *. ” 作为二级域名的通配符", "ProxyBadRequestHealthCheckMessage": "测试代理失败。状态码:{statusCode}", "ProxyFailedToTestHealthCheckMessage": "测试代理失败: {url}", "ProxyResolveIpHealthCheckMessage": "无法解析已设置的代理服务器主机{proxyHostName}的IP地址", @@ -322,7 +322,7 @@ "Queue": "队列", "Queued": "队列中", "Rss": "RSS", - "RssIsNotSupportedWithThisIndexer": "该搜刮器不支持RSS", + "RssIsNotSupportedWithThisIndexer": "该索引器不支持 RSS", "RawSearchSupported": "‎支持原始‎‎搜索‎", "ReadTheWikiForMoreInformation": "查阅Wiki获得更多信息", "Reddit": "Reddit", @@ -368,7 +368,7 @@ "Season": "季", "Security": "安全", "Seeders": "种子", - "SelectAll": "选择全部", + "SelectAll": "全选", "SemiPrivate": "‎半私有‎", "SendAnonymousUsageData": "发送匿名使用数据", "SetTags": "设置标签", @@ -389,13 +389,13 @@ "SettingsShowRelativeDatesHelpText": "显示相对日期(今天昨天等)或绝对日期", "SettingsSqlLoggingHelpText": "记录来自{appName}的所有SQL查询", "SettingsTimeFormat": "时间格式", - "ShowAdvanced": "显示高级设置", + "ShowAdvanced": "高级设置", "ShowSearch": "显示搜索", "ShowSearchHelpText": "悬停时显示搜索按钮", "Shutdown": "关机", "Size": "大小", "Sort": "排序", - "Source": "来源", + "Source": "代码", "StartTypingOrSelectAPathBelow": "输入路径或者从下面选择", "Started": "已开始", "StartupDirectory": "启动目录", @@ -411,7 +411,7 @@ "SyncProfile": "同步配置文件", "SyncProfiles": "同步配置文件", "System": "系统", - "SystemTimeCheckMessage": "系统时间相差超过1天。在纠正时间之前,计划的任务可能无法正确运行", + "SystemTimeHealthCheckMessage": "系统时间相差超过1天。在纠正时间之前,计划的任务可能无法正确运行", "TVSearchTypes": "‎电视‎‎搜索‎‎类型‎", "TableOptions": "表格选项", "TableOptionsColumnsMessage": "选择显示哪些列并排序", @@ -425,8 +425,8 @@ "TestAll": "测试全部", "TestAllApps": "测试全部应用", "TestAllClients": "测试全部客户端", - "TestAllIndexers": "测试全部搜刮器", - "TheLatestVersionIsAlreadyInstalled": "已安装最新版本的{appName}", + "TestAllIndexers": "测试全部索引器", + "OnLatestVersion": "已安装最新版本的{appName}", "Theme": "主题", "ThemeHelpText": "更改应用程序UI主题,“自动”主题将使用您的操作系统主题设置亮或暗模式。灵感来源于{inspirredby}。", "Time": "时间", @@ -451,7 +451,7 @@ "UnableToAddANewIndexerProxyPleaseTryAgain": "无法添加搜刮器,请稍后重试。", "UnableToAddANewNotificationPleaseTryAgain": "无法添加新通知,请稍后重试。", "UnableToLoadAppProfiles": "无法加载应用配置", - "UnableToLoadBackups": "无法加载备份", + "BackupsLoadError": "无法加载备份", "UnableToLoadDevelopmentSettings": "无法加载开发设置", "DownloadClientsLoadError": "无法加载下载客户端", "UnableToLoadGeneralSettings": "无法加载通用设置", @@ -462,8 +462,8 @@ "UnableToLoadTags": "无法加载标签", "UnableToLoadUISettings": "无法加载UI设置", "UnsavedChanges": "未保存更改", - "UnselectAll": "取消选择全部", - "UpdateAutomaticallyHelpText": "自动下载并安装更新。你还可以在“系统:更新”中安装", + "UnselectAll": "取消全选", + "UpdateAutomaticallyHelpText": "自动下载并安装更新。您还可以在「“系统”->“更新”」中安装", "UpdateAvailableHealthCheckMessage": "有新的更新可用", "UpdateStartupNotWritableHealthCheckMessage": "无法安装更新,因为用户“{userName}”对于启动文件夹“{startupFolder}”没有写入权限。", "UpdateStartupTranslocationHealthCheckMessage": "无法安装更新,因为启动文件夹“{0}”在一个应用程序迁移文件夹。Cannot install update because startup folder '{startupFolder}' is in an App Translocation folder.", @@ -484,7 +484,7 @@ "Warn": "警告", "Website": "‎网站‎", "Wiki": "Wiki", - "Year": "年", + "Year": "年份", "Yes": "确定", "YesCancel": "确定,取消", "Yesterday": "昨天", @@ -493,9 +493,9 @@ "ApplyChanges": "应用更改", "ApplyTagsHelpTextAdd": "添加: 添加标签至已有的标签列表中", "CountDownloadClientsSelected": "已选择 {count} 个下载客户端", - "CountIndexersSelected": "已选择 {count} 个索引器", + "CountIndexersSelected": "选定 {count} 个索引器", "DeleteSelectedDownloadClientsMessageText": "您确定要删除 {count} 个选定的下载客户端吗?", - "DeleteSelectedIndexersMessageText": "您确定要删除{count}选定的索引器吗?", + "DeleteSelectedIndexersMessageText": "您确定要删除选定的 {count} 个索引器吗?", "EditSelectedDownloadClients": "编辑选定的下载客户端", "Implementation": "执行", "ManageDownloadClients": "管理下载客户端", @@ -503,12 +503,12 @@ "NoIndexersFound": "未找到索引器", "SelectIndexers": "搜刮器搜索", "ApplyTagsHelpTextHowToApplyApplications": "如何给选中的电影添加标签", - "ApplyTagsHelpTextHowToApplyIndexers": "如何将标签应用到已选择的索引器", + "ApplyTagsHelpTextHowToApplyIndexers": "如何将标签应用到已选中的索引器", "ApplyTagsHelpTextReplace": "替换: 用输入的标签替换当前标签 (不输入将会清除所有标签)", "DeleteSelectedApplicationsMessageText": "您确定要删除{count}选定的应用程序吗?", "DeleteSelectedDownloadClients": "删除下载客户端", "DownloadClientPriorityHelpText": "优先考虑多个下载客户端,循环查询用于具有相同优先级的客户端。", - "EditSelectedIndexers": "编辑选定的索引器", + "EditSelectedIndexers": "编辑选定索引器", "OnHealthRestored": "健康度恢复", "OnHealthRestoredHelpText": "健康度恢复", "ApplyTagsHelpTextRemove": "移除: 移除已输入的标签", @@ -559,11 +559,11 @@ "AddConnectionImplementation": "添加连接- {implementationName}", "AddDownloadClientImplementation": "添加下载客户端- {implementationName}", "AddIndexerProxyImplementation": "添加搜刮器代理-{实体名称}", - "AppUpdated": "{appName} 升级", + "AppUpdated": "{appName} 已升级", "EditDownloadClientImplementation": "编辑下载客户端- {implementationName}", "EditIndexerImplementation": "编辑索引器- {implementationName}", "EditIndexerProxyImplementation": "添加搜刮器代理-{实体名称}", - "AppUpdatedVersion": "{appName} 已经更新到 {version} 版本,重新加载 {appName} 使更新生效", + "AppUpdatedVersion": "{appName} 已经更新到版本 {version} ,重新加载 {appName} 使更新生效", "EditApplicationImplementation": "添加应用-{实体名称}", "EditConnectionImplementation": "编辑连接- {implementationName}", "NotificationStatusAllClientHealthCheckMessage": "由于故障所用应用程序都不可用", @@ -571,7 +571,7 @@ "AuthBasic": "基础(浏览器弹出对话框)", "AuthForm": "表单(登陆页面)", "AuthenticationMethod": "认证方式", - "AuthenticationMethodHelpTextWarning": "请选择一个有效的身份验证方式", + "AuthenticationMethodHelpTextWarning": "请选择一个有效的认证方式", "AuthenticationRequiredPasswordHelpTextWarning": "请输入新密码", "AuthenticationRequiredUsernameHelpTextWarning": "请输入新用户名", "Clone": "复制", @@ -580,7 +580,7 @@ "External": "外部的", "None": "无", "ResetAPIKeyMessageText": "您确定要重置您的 API 密钥吗?", - "IndexerDownloadClientHealthCheckMessage": "有无效下载客户端的索引器:{indexerNames}。", + "IndexerDownloadClientHealthCheckMessage": "使用无效下载客户端的索引器:{indexerNames}。", "ApplicationTagsHelpTextWarning": "标签应该谨慎使用,它们可能会产生意想不到的效果。带有标签的应用程序只会与具有相同标签的索引器同步。", "EditCategory": "编辑分类", "IndexerHistoryLoadError": "加载索引器历史记录出错", @@ -599,11 +599,160 @@ "PackSeedTime": "做种时间", "PasswordConfirmation": "确认密码", "AuthenticationRequiredPasswordConfirmationHelpTextWarning": "确认新密码", - "InvalidUILanguage": "您的UI设置为无效语言,请纠正并保存设置", + "InvalidUILanguage": "您的 UI 设置为无效语言,请纠正并保存设置", "NoIndexerCategories": "没有找到此索引器的分类", "DownloadClientQbittorrentSettingsContentLayout": "内容布局", "DownloadClientQbittorrentSettingsContentLayoutHelpText": "是否使用 qBittorrent 配置的内容布局,使用种子的原始布局或始终创建子文件夹(qBittorrent 4.3.2+)", - "DownloadClientAriaSettingsDirectoryHelpText": "可选的下载位置,留空使用 Aria2 默认位置", + "DownloadClientAriaSettingsDirectoryHelpText": "下载位置可选择,留空使用 Aria2 默认位置", "ManageClients": "管理客户端", - "CustomFilter": "自定义过滤器" + "CustomFilter": "自定义过滤器", + "BlackholeFolderHelpText": "{appName} 将在其中存储 {extension} 文件的文件夹", + "ApplicationSettingsSyncRejectBlocklistedTorrentHashes": "在抓取时不会同步黑名单种子", + "UsenetBlackholeNzbFolder": "NZB文件夹", + "DownloadClientFreeboxSettingsAppIdHelpText": "创建访问 Freebox API 所需的 App ID(即 “app_id”)", + "DownloadClientNzbgetSettingsAddPausedHelpText": "此选项至少需要 NzbGet 版本 16.0", + "DownloadClientQbittorrentSettingsSequentialOrderHelpText": "按顺序下载文件(qBittorrent 4.1.0+)", + "TorrentBlackholeSaveMagnetFilesHelpText": "如果没有可用的 .torrent 文件,请保存磁力链接(仅当下载客户端支持保存到文件的磁力连接时才有用)", + "DownloadClientFloodSettingsAdditionalTags": "附加标签", + "DownloadClientFreeboxSettingsApiUrl": "API 地址", + "DownloadClientFreeboxSettingsAppId": "App ID", + "DownloadClientDelugeSettingsUrlBaseHelpText": "向 Deluge JSON URL 添加前缀,请参阅 {url}", + "DownloadClientFloodSettingsUrlBaseHelpText": "为 Flood API 添加前缀,例如 {url}", + "DownloadClientFreeboxSettingsAppToken": "App Token", + "DownloadClientFreeboxSettingsAppTokenHelpText": "创建访问 Freebox API 所需的 App token(即 “ app_token”)", + "DownloadClientQbittorrentSettingsInitialStateHelpText": "添加到 qBittorrent 的种子的初始状态。 请注意,强制做种不遵守种子限制", + "GrabRelease": "抓取版本", + "ManualGrab": "手动抓取", + "OverrideAndAddToDownloadClient": "覆盖并添加到下载队列", + "OverrideGrabModalTitle": "覆盖并抓取 - {title}", + "PrioritySettings": "优先级: {priority}", + "SelectDownloadClientModalTitle": "{modalTitle} - 选择下载客户端", + "Donate": "捐赠", + "DownloadClientPneumaticSettingsNzbFolder": "NZB文件夹", + "DownloadClientPneumaticSettingsNzbFolderHelpText": "需要从 XBMC 访问此文件夹", + "DownloadClientQbittorrentSettingsSequentialOrder": "按顺序下载", + "DownloadClientRTorrentSettingsAddStopped": "添加后暂停", + "DownloadClientRTorrentSettingsAddStoppedHelpText": "启用将在停止状态下向 rTorrent 添加 torrent 和磁力链接。 这可能会破坏磁力文件。", + "DownloadClientRTorrentSettingsDirectoryHelpText": "用于放置下载的可选位置,留空以使用默认的 rTorrent 位置", + "DownloadClientRTorrentSettingsUrlPath": "URL 地址", + "IndexerSettingsSeedRatio": "做种比率", + "SecretToken": "密钥令牌", + "XmlRpcPath": "XML RPC 路径", + "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "如果 torrent 的哈希被屏蔽了,某些索引器在使用RSS或者搜索期间可能无法正确拒绝它,启用此功能将允许在抓取 torrent 之后但在将其发送到客户端之前拒绝它。", + "DownloadClientDownloadStationSettingsDirectoryHelpText": "用于存放下载内容的共享文件夹可选择,留空使用默认的 Download Station 位置", + "DownloadClientFloodSettingsAdditionalTagsHelpText": "添加媒体属性作为标签。 提示是示例。", + "DownloadClientFreeboxSettingsApiUrlHelpText": "使用 API 版本定义 Freebox API 基本 URL,例如 “{url}”,默认为 “{defaultApiUrl}”", + "DownloadClientPneumaticSettingsStrmFolder": "Strm 文件夹", + "DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText": "首先下载第一个和最后一个片段(qBittorrent 4.1.0+)", + "DownloadClientQbittorrentSettingsUseSslHelpText": "使用安全连接。 请参阅 qBittorrent 中的「选项 -> Web UI -> “使用 HTTPS 而不是 HTTP”」。", + "DownloadClientRTorrentSettingsUrlPathHelpText": "XMLRPC 端点的路径,请参阅 {url}。 使用 ruTorrent 时,这通常是 RPC2 或 [ruTorrent 路径]{url2}。", + "DownloadClientSettingsDestinationHelpText": "手动指定下载目录,留空使用默认值", + "DownloadClientSettingsInitialState": "初始状态", + "DownloadClientSettingsInitialStateHelpText": "添加到 {clientName} 的种子初始状态", + "DownloadClientTransmissionSettingsDirectoryHelpText": "可选的下载位置,留空以使用默认传输位置", + "DownloadClientTransmissionSettingsUrlBaseHelpText": "向 {clientName} RPC URL 添加前缀,例如 {url},默认为 '{defaultUrl}'", + "IndexerSettingsAppsMinimumSeeders": "应用程序最少种子数", + "IndexerSettingsPackSeedTimeIndexerHelpText": "种子下载的时间(季或专辑)应在停止前保持上传状态,应用程序默认设定为empty", + "IndexerHDBitsSettingsOriginsHelpText": "如果未指定,则使用所有选项。", + "DownloadClientFreeboxSettingsHostHelpText": "Freebox 的主机名或主机 IP 地址,默认为 “{url}”(仅在同一网络上有效)", + "DownloadClientFreeboxSettingsPortHelpText": "用于访问 Freebox 接口的端口,默认为 '{port}'", + "DownloadClientQbittorrentSettingsFirstAndLastFirst": "先下载首尾文件块", + "DownloadClientSettingsAddPaused": "添加并暂停", + "DownloadClientSettingsUrlBaseHelpText": "向 {clientName} url 添加前缀,例如 {url}", + "DownloadClientSettingsUseSslHelpText": "连接到 {clientName} 时使用安全连接", + "IndexerBeyondHDSettingsSearchTypes": "搜索类型", + "IndexerSettingsApiPath": "API 路径", + "IndexerSettingsApiPathHelpText": "API 的路径,通常是 {url}", + "IndexerSettingsAppsMinimumSeedersHelpText": "使用索引器抓去所需的最低种子数,如果为空,则同步配置文件的默认值", + "IndexerSettingsAdditionalParameters": "附加参数", + "IndexerSettingsSeedTime": "做种时间", + "IndexerSettingsVipExpiration": "VIP过期", + "Menu": "菜单", + "Mixed": "混合", + "TorrentBlackholeSaveMagnetFilesExtension": "保存磁力链接文件扩展名", + "TorrentBlackholeSaveMagnetFilesExtensionHelpText": "用于磁力链接的扩展名,默认为 “.magnet”", + "Destination": "目标", + "Directory": "目录", + "IndexerHDBitsSettingsCodecs": "编解码器", + "IndexerHDBitsSettingsMediums": "媒介", + "IndexerHDBitsSettingsMediumsHelpText": "如果未指定,则使用所有选项。", + "IndexerSettingsPackSeedTime": "做种时间", + "IndexerSettingsCookie": "Cookie", + "ProxyValidationBadRequest": "测试代理失败。状态码:{statusCode}", + "ProxyValidationUnableToConnect": "无法连接到索引器:{exceptionMessage}。 检查有关此错误的日志以了解详细信息", + "DownloadClientFloodSettingsTagsHelpText": "下载的初始标签。下载必须具有所有初始标签才可被识别。 这可以避免与不相关的下载发生冲突。", + "DownloadClientPneumaticSettingsStrmFolderHelpText": "该文件夹中的 .strm 文件将由 drone 导入", + "IndexerHDBitsSettingsCodecsHelpText": "如果未指定,则使用所有选项。", + "IndexerSettingsSeedRatioHelpText": "停止之前应达到的做种比率,留空使用下载客户端的默认值。 比率应至少为 1.0 并遵循索引器规则", + "IndexerSettingsSeedTimeHelpText": "停止前应做种的时间,留空使用下载客户端的默认值", + "TorrentBlackholeSaveMagnetFiles": "保存磁力链接文件", + "TorrentBlackholeTorrentFolder": "种子文件夹", + "UseSsl": "使用 SSL", + "Default": "默认", + "Any": "任何", + "BuiltIn": "内置的", + "Script": "脚本", + "InfoUrl": "信息 URL", + "PublishedDate": "发布日期", + "Redirected": "重定向", + "AllSearchResultsHiddenByFilter": "根据过滤条件所有结果已隐藏", + "HealthMessagesInfoBox": "您可以通过单击行尾的wiki链接(图书图标)或检查[日志]({link})来查找有关这些运行状况检查消息原因的更多信息。如果你在理解这些信息方面有困难,你可以通过下面的链接联系我们的支持。", + "PackageVersionInfo": "{packageVersion} 由 {packageAuthor} 制作", + "LabelIsRequired": "需要标签", + "LogSizeLimit": "日志大小限制", + "LogSizeLimitHelpText": "存档前的最大日志文件大小(MB)。默认值为 1 MB。", + "NotificationsTelegramSettingsIncludeAppName": "标题中包含 {appName}", + "NotificationsTelegramSettingsIncludeAppNameHelpText": "可选,在消息标题前加上 {appName} 以区分来自不同应用的通知", + "NotificationsEmailSettingsUseEncryption": "启用加密", + "NotificationsEmailSettingsUseEncryptionHelpText": "是否优先使用加密(如果服务器已配置),始终使用通过SSL(仅端口465)或StartTLS(任何其他端口)进行加密,或从不使用加密", + "ClickToChangeQueryOptions": "单击以更改查询选项", + "ApplicationsLoadError": "无法加载应用程序列表", + "DownloadClientSettingsDefaultCategoryHelpText": "默认的备用分类,当发布资源没有匹配的分类时将使用此分类。为 {appName} 添加一个特定的分类,可以避免与非 {appName} 的无关下载发生冲突。分类是可选的,但强烈建议使用。", + "AverageGrabs": "平均抓取次数", + "AverageQueries": "平均查询次数", + "DefaultCategory": "默认分类", + "DownloadClientSettingsDefaultCategorySubFolderHelpText": "默认的备用分类,当发布资源没有匹配的分类时将使用此分类。为 {appName} 添加一个特定的分类,可以避免与非 {appName} 的无关下载发生冲突。分类是可选的,但强烈建议使用。启用分类后,会在输出目录中创建一个 [分类] 子目录。", + "ErrorRestoringBackup": "恢复备份错误", + "UpdaterLogFiles": "更新器日志文件", + "WouldYouLikeToRestoreBackup": "是否要还原备份 “{name}”?", + "AptUpdater": "使用apt安装更新", + "DockerUpdater": "更新Docker容器以更新应用", + "Download": "下载", + "ExternalUpdater": "{appName}配置为使用外部更新机制", + "FailedToFetchUpdates": "获取更新失败", + "LogFilesLocation": "日志文件位于: {location}", + "Logout": "注销", + "NoEventsFound": "无事件", + "RestartReloadNote": "注意:{appName}将在恢复过程中自动重启并重新加载UI。", + "TheLogLevelDefault": "默认的日志等级为 \"Info\",可以在 [常规设置] 中修改 (/settings/general)", + "UpdateAppDirectlyLoadError": "无法直接更新{appName},", + "InstallLatest": "安装最新版", + "Install": "安装", + "InstallMajorVersionUpdate": "安装更新", + "InstallMajorVersionUpdateMessage": "此更新将安装新的主要版本,这可能与您的系统不兼容。您确定要安装此更新吗?", + "InstallMajorVersionUpdateMessageLink": "请查看 [{domain}]({url}) 以获取更多信息。", + "CurrentlyInstalled": "已安装", + "PreviouslyInstalled": "上次安装", + "FailedToFetchSettings": "设置同步失败", + "DownloadClientSettingsPriorityItemHelpText": "抓取内容时优先使用", + "IndexerAlphaRatioSettingsExcludeScene": "排除场景", + "IndexerAlphaRatioSettingsExcludeSceneHelpText": "从结果中排除场景版本", + "IndexerAlphaRatioSettingsFreeleechOnlyHelpText": "只搜索免费发布", + "IndexerBeyondHDSettingsFreeleechOnlyHelpText": "只搜索免费发布", + "IndexerBeyondHDSettingsLimitedOnly": "仅限", + "IndexerBeyondHDSettingsApiKeyHelpText": "来自网站的API密钥(在我的安全 => API密钥)", + "IndexerAvistazSettingsFreeleechOnlyHelpText": "只搜索免费发布", + "IndexerAvistazSettingsPasswordHelpText": "网站密码", + "IndexerAvistazSettingsPidHelpText": "我的帐户或个人资料页的PID", + "IndexerAvistazSettingsUsernameHelpText": "网站用户名", + "IndexerAvistazSettingsUsernameHelpTextWarning": "只有成员级别及以上才能使用此索引器上的API。", + "IndexerBeyondHDSettingsLimitedOnlyHelpText": "仅限免费搜索(有限UL)", + "IndexerGazelleGamesSettingsFreeleechOnlyHelpText": "只搜索免费发布", + "IndexerMTeamTpSettingsFreeleechOnlyHelpText": "只搜索免费发布", + "IndexerIPTorrentsSettingsFreeleechOnlyHelpText": "只搜索免费发布", + "IndexerHDBitsSettingsUsernameHelpText": "网站用户名", + "IndexerPassThePopcornSettingsFreeleechOnlyHelpText": "只搜索免费发布", + "IndexerFileListSettingsFreeleechOnlyHelpText": "只搜索免费发布", + "IndexerFileListSettingsUsernameHelpText": "网站用户名", + "IndexerBeyondHDSettingsRefundOnlyHelpText": "Search refund only" } diff --git a/src/NzbDrone.Core/Localization/Core/zh_Hans.json b/src/NzbDrone.Core/Localization/Core/zh_Hans.json new file mode 100644 index 000000000..54e88a350 --- /dev/null +++ b/src/NzbDrone.Core/Localization/Core/zh_Hans.json @@ -0,0 +1,7 @@ +{ + "About": "关于", + "Add": "添加", + "Analytics": "分析", + "Username": "用户名", + "AcceptConfirmationModal": "中文" +} diff --git a/src/NzbDrone.Core/Localization/Core/zh_TW.json b/src/NzbDrone.Core/Localization/Core/zh_TW.json index f83513498..0d1a65581 100644 --- a/src/NzbDrone.Core/Localization/Core/zh_TW.json +++ b/src/NzbDrone.Core/Localization/Core/zh_TW.json @@ -2,17 +2,17 @@ "About": "關於", "Add": "新增", "Added": "已新增", - "Actions": "執行", + "Actions": "動作", "Age": "年齡", "AddIndexer": "新增索引", "AddNewIndexer": "新增新索引", - "AddDownloadClient": "新增下載器", + "AddDownloadClient": "加入下載用戶端", "Analytics": "分析", "AddIndexerProxy": "新增索引器代理", "AddingTag": "新增標籤", "All": "全部", "AddRemoveOnly": "僅限新增或移除", - "AcceptConfirmationModal": "接受確認模式", + "AcceptConfirmationModal": "接受確認對話框", "Language": "語言", "Filter": "篩選", "Reload": "重新載入", @@ -92,7 +92,7 @@ "Queued": "佇列", "Replace": "替換", "Scheduled": "已排程", - "UI": "UI", + "UI": "使用者介面", "Apply": "套用", "Connections": "連接", "Reset": "重置", @@ -112,7 +112,7 @@ "UnableToAddANewAppProfilePleaseTryAgain": "無法加入新的條件,請重新嘗試。", "UnableToAddANewApplicationPleaseTryAgain": "無法加入新的條件,請重新嘗試。", "UnableToAddANewDownloadClientPleaseTryAgain": "無法加入新的條件,請重新嘗試。", - "UnableToAddANewIndexerPleaseTryAgain": "無法加入新的條件,請重新嘗試。", + "UnableToAddANewIndexerPleaseTryAgain": "無法加入新的索引器,請重新嘗試。", "UnableToAddANewIndexerProxyPleaseTryAgain": "無法加入新的條件,請重新嘗試。", "AddApplicationImplementation": "新增連接 - {implementationName}", "AddIndexerProxyImplementation": "新增索引 - {implementationName}", @@ -128,5 +128,41 @@ "ApplyChanges": "應用", "ApplyTagsHelpTextHowToApplyApplications": "如何套用標籤在所選擇的輸入清單", "Artist": "演員", - "Id": "ID" + "Id": "ID", + "Usenet": "Usenet", + "ApplyTagsHelpTextHowToApplyIndexers": "如何套用標籤在所選擇的輸入清單", + "Docker": "Docker", + "IndexerHDBitsSettingsCodecs": "編解碼器", + "Directory": "目錄", + "BuiltIn": "內建的", + "AllSearchResultsHiddenByFilter": "根據所使用的篩選器已將所有結果隱藏", + "AptUpdater": "使用apt安裝更新", + "Discord": "Discord", + "Any": "任何", + "UpdateAppDirectlyLoadError": "無法直接更新 {appName},", + "UnselectAll": "取消全選", + "Uptime": "上線時間", + "AdvancedSettingsHiddenClickToShow": "進階設定已隱藏,點擊以顯示", + "UnableToLoadIndexers": "無法載入索引器", + "UnableToLoadHistory": "無法載入歷史記錄", + "UnableToLoadTags": "無法載入標籤", + "UILanguage": "使用者介面語言", + "UILanguageHelpText": "{appName} 介面所使用的語言", + "UISettings": "使用者介面設定", + "UnableToLoadUISettings": "無法載入 UI 設定", + "AddCustomFilter": "新增自定義過濾器", + "AddApplication": "新增應用程式", + "AddDownloadClientToProwlarr": "新增一個下載客戶端以允許 {appName} 從 UI 直接傳送手動搜尋結果。", + "AddCategory": "新增類別", + "AdvancedSettingsShownClickToHide": "進階設定已顯示,點擊以隱藏", + "Version": "版本", + "UpdateAvailableHealthCheckMessage": "可用的新版本: {version}", + "AuthenticationRequiredHelpText": "更改需要進行驗證的請求。除非你了解其中的風險,否則請勿修改。", + "AuthenticationRequiredPasswordHelpTextWarning": "請輸入新密碼", + "AuthenticationRequiredUsernameHelpTextWarning": "請輸入新用戶名", + "AuthenticationRequiredPasswordConfirmationHelpTextWarning": "確認新密碼", + "AuthenticationRequired": "需要驗證", + "AuthenticationRequiredWarning": "為防止未經認證的遠程訪問,{appName} 現需要啟用身份認證。您可以選擇禁用本地地址的身份認證。", + "AuthenticationMethodHelpTextWarning": "請選擇一個有效的驗證方式", + "AuthenticationMethod": "驗證方式" } diff --git a/src/NzbDrone.Core/Messaging/Commands/CommandQueueManager.cs b/src/NzbDrone.Core/Messaging/Commands/CommandQueueManager.cs index 12ce4cb06..6ad659294 100644 --- a/src/NzbDrone.Core/Messaging/Commands/CommandQueueManager.cs +++ b/src/NzbDrone.Core/Messaging/Commands/CommandQueueManager.cs @@ -105,6 +105,8 @@ namespace NzbDrone.Core.Messaging.Commands _logger.Trace("Publishing {0}", command.Name); _logger.Trace("Checking if command is queued or started: {0}", command.Name); + command.Trigger = trigger; + lock (_commandQueue) { var existingCommands = QueuedOrStarted(command.Name); @@ -141,7 +143,6 @@ namespace NzbDrone.Core.Messaging.Commands var command = GetCommand(commandName); command.LastExecutionTime = lastExecutionTime; command.LastStartTime = lastStartTime; - command.Trigger = trigger; return Push(command, priority, trigger); } @@ -232,13 +233,13 @@ namespace NzbDrone.Core.Messaging.Commands _repo.Trim(); } - private dynamic GetCommand(string commandName) + private Command GetCommand(string commandName) { commandName = commandName.Split('.').Last(); var commands = _knownTypes.GetImplementations(typeof(Command)); var commandType = commands.Single(c => c.Name.Equals(commandName, StringComparison.InvariantCultureIgnoreCase)); - return Json.Deserialize("{}", commandType); + return Json.Deserialize("{}", commandType) as Command; } private void Update(CommandModel command, CommandStatus status, string message) diff --git a/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs b/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs index 7ca1dc8fc..04bac39f0 100755 --- a/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs +++ b/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; +using System.Linq; using FluentValidation.Results; using NLog; using NzbDrone.Common.Disk; @@ -38,7 +39,31 @@ namespace NzbDrone.Core.Notifications.CustomScript public override string Link => "https://wiki.servarr.com/prowlarr/settings#connections"; - public override ProviderMessage Message => new ProviderMessage("Testing will execute the script with the EventType set to Test, ensure your script handles this correctly", ProviderMessageType.Warning); + public override ProviderMessage Message => new ("Testing will execute the script with the EventType set to Test, ensure your script handles this correctly", ProviderMessageType.Warning); + + public override void OnGrab(GrabMessage message) + { + var environmentVariables = new StringDictionary(); + + environmentVariables.Add("Prowlarr_EventType", "Grab"); + environmentVariables.Add("Prowlarr_InstanceName", _configFileProvider.InstanceName); + environmentVariables.Add("Prowlarr_ApplicationUrl", _configService.ApplicationUrl); + environmentVariables.Add("Prowlarr_Release_Title", message.Release.Title); + environmentVariables.Add("Prowlarr_Release_Indexer", message.Release.Indexer ?? string.Empty); + environmentVariables.Add("Prowlarr_Release_Size", message.Release.Size.ToString()); + environmentVariables.Add("Prowlarr_Release_Genres", string.Join("|", message.Release.Genres)); + environmentVariables.Add("Prowlarr_Release_Categories", string.Join("|", message.Release.Categories.Select(f => f.Name))); + environmentVariables.Add("Prowlarr_Release_IndexerFlags", string.Join("|", message.Release.IndexerFlags.Select(f => f.Name))); + environmentVariables.Add("Prowlarr_Release_PublishDate", message.Release.PublishDate.ToUniversalTime().ToString("s") + "Z"); + environmentVariables.Add("Prowlarr_Download_Client", message.DownloadClientName ?? string.Empty); + environmentVariables.Add("Prowlarr_Download_Client_Type", message.DownloadClientType ?? string.Empty); + environmentVariables.Add("Prowlarr_Download_Id", message.DownloadId ?? string.Empty); + environmentVariables.Add("Prowlarr_Source", message.Source ?? string.Empty); + environmentVariables.Add("Prowlarr_Host", message.Host ?? string.Empty); + environmentVariables.Add("Prowlarr_Redirect", message.Redirect.ToString()); + + ExecuteScript(environmentVariables); + } public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck) { @@ -130,10 +155,5 @@ namespace NzbDrone.Core.Notifications.CustomScript return processOutput; } - - private bool ValidatePathParent(string possibleParent, string path) - { - return possibleParent.IsParentPath(path); - } } } diff --git a/src/NzbDrone.Core/Notifications/Discord/Discord.cs b/src/NzbDrone.Core/Notifications/Discord/Discord.cs index 9eb41e989..48b93f35d 100644 --- a/src/NzbDrone.Core/Notifications/Discord/Discord.cs +++ b/src/NzbDrone.Core/Notifications/Discord/Discord.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using FluentValidation.Results; using NzbDrone.Common.Extensions; +using NzbDrone.Core.Configuration; using NzbDrone.Core.Notifications.Discord.Payloads; using NzbDrone.Core.Validation; @@ -10,10 +11,12 @@ namespace NzbDrone.Core.Notifications.Discord public class Discord : NotificationBase<DiscordSettings> { private readonly IDiscordProxy _proxy; + private readonly IConfigFileProvider _configFileProvider; - public Discord(IDiscordProxy proxy) + public Discord(IDiscordProxy proxy, IConfigFileProvider configFileProvider) { _proxy = proxy; + _configFileProvider = configFileProvider; } public override string Name => "Discord"; @@ -22,18 +25,18 @@ namespace NzbDrone.Core.Notifications.Discord public override void OnGrab(GrabMessage message) { var embed = new Embed - { - Author = new DiscordAuthor - { - Name = Settings.Author.IsNullOrWhiteSpace() ? Environment.MachineName : Settings.Author, - IconUrl = "https://raw.githubusercontent.com/Prowlarr/Prowlarr/develop/Logo/256.png" - }, - Title = RELEASE_GRABBED_TITLE, - Description = message.Message, - Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), - Color = message.Successful ? (int)DiscordColors.Success : (int)DiscordColors.Danger, - Fields = new List<DiscordField>() - }; + { + Author = new DiscordAuthor + { + Name = Settings.Author.IsNullOrWhiteSpace() ? _configFileProvider.InstanceName : Settings.Author, + IconUrl = "https://raw.githubusercontent.com/Prowlarr/Prowlarr/develop/Logo/256.png" + }, + Title = RELEASE_GRABBED_TITLE, + Description = message.Message, + Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), + Color = message.Successful ? (int)DiscordColors.Success : (int)DiscordColors.Danger, + Fields = new List<DiscordField>() + }; foreach (var field in Settings.GrabFields) { @@ -80,81 +83,72 @@ namespace NzbDrone.Core.Notifications.Discord public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck) { - var attachments = new List<Embed> - { - new Embed - { - Author = new DiscordAuthor - { - Name = Settings.Author.IsNullOrWhiteSpace() ? Environment.MachineName : Settings.Author, - IconUrl = "https://raw.githubusercontent.com/Prowlarr/Prowlarr/develop/Logo/256.png" - }, - Title = healthCheck.Source.Name, - Description = healthCheck.Message, - Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), - Color = healthCheck.Type == HealthCheck.HealthCheckResult.Warning ? (int)DiscordColors.Warning : (int)DiscordColors.Danger - } - }; + var embed = new Embed + { + Author = new DiscordAuthor + { + Name = Settings.Author.IsNullOrWhiteSpace() ? _configFileProvider.InstanceName : Settings.Author, + IconUrl = "https://raw.githubusercontent.com/Prowlarr/Prowlarr/develop/Logo/256.png" + }, + Title = healthCheck.Source.Name, + Description = healthCheck.Message, + Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), + Color = healthCheck.Type == HealthCheck.HealthCheckResult.Warning ? (int)DiscordColors.Warning : (int)DiscordColors.Danger + }; - var payload = CreatePayload(null, attachments); + var payload = CreatePayload(null, new List<Embed> { embed }); _proxy.SendPayload(payload, Settings); } public override void OnHealthRestored(HealthCheck.HealthCheck previousCheck) { - var attachments = new List<Embed> - { - new Embed - { - Author = new DiscordAuthor - { - Name = Settings.Author.IsNullOrWhiteSpace() ? Environment.MachineName : Settings.Author, - IconUrl = "https://raw.githubusercontent.com/Prowlarr/Prowlarr/develop/Logo/256.png" - }, - Title = "Health Issue Resolved: " + previousCheck.Source.Name, - Description = $"The following issue is now resolved: {previousCheck.Message}", - Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), - Color = (int)DiscordColors.Success - } - }; + var embed = new Embed + { + Author = new DiscordAuthor + { + Name = Settings.Author.IsNullOrWhiteSpace() ? _configFileProvider.InstanceName : Settings.Author, + IconUrl = "https://raw.githubusercontent.com/Prowlarr/Prowlarr/develop/Logo/256.png" + }, + Title = "Health Issue Resolved: " + previousCheck.Source.Name, + Description = $"The following issue is now resolved: {previousCheck.Message}", + Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), + Color = (int)DiscordColors.Success + }; - var payload = CreatePayload(null, attachments); + var payload = CreatePayload(null, new List<Embed> { embed }); _proxy.SendPayload(payload, Settings); } public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) { - var attachments = new List<Embed> - { - new Embed - { - Author = new DiscordAuthor - { - Name = Settings.Author.IsNullOrWhiteSpace() ? Environment.MachineName : Settings.Author, - IconUrl = "https://raw.githubusercontent.com/Prowlarr/Prowlarr/develop/Logo/256.png" - }, - Title = APPLICATION_UPDATE_TITLE, - Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), - Color = (int)DiscordColors.Standard, - Fields = new List<DiscordField>() - { - new DiscordField() - { - Name = "Previous Version", - Value = updateMessage.PreviousVersion.ToString() - }, - new DiscordField() - { - Name = "New Version", - Value = updateMessage.NewVersion.ToString() - } - }, - } - }; + var embed = new Embed + { + Author = new DiscordAuthor + { + Name = Settings.Author.IsNullOrWhiteSpace() ? _configFileProvider.InstanceName : Settings.Author, + IconUrl = "https://raw.githubusercontent.com/Prowlarr/Prowlarr/develop/Logo/256.png" + }, + Title = APPLICATION_UPDATE_TITLE, + Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), + Color = (int)DiscordColors.Standard, + Fields = new List<DiscordField> + { + new () + { + Name = "Previous Version", + Value = updateMessage.PreviousVersion.ToString() + }, + new () + { + Name = "New Version", + Value = updateMessage.NewVersion.ToString() + } + }, + }; - var payload = CreatePayload(null, attachments); + var payload = CreatePayload(null, new List<Embed> { embed }); _proxy.SendPayload(payload, Settings); } @@ -208,19 +202,5 @@ namespace NzbDrone.Core.Notifications.Discord return payload; } - - private static string BytesToString(long byteCount) - { - string[] suf = { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; //Longs run out around EB - if (byteCount == 0) - { - return "0 " + suf[0]; - } - - var bytes = Math.Abs(byteCount); - var place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024))); - var num = Math.Round(bytes / Math.Pow(1024, place), 1); - return string.Format("{0} {1}", (Math.Sign(byteCount) * num).ToString(), suf[place]); - } } } diff --git a/src/NzbDrone.Core/Notifications/Gotify/Gotify.cs b/src/NzbDrone.Core/Notifications/Gotify/Gotify.cs index 5ee380e8b..54380dae3 100644 --- a/src/NzbDrone.Core/Notifications/Gotify/Gotify.cs +++ b/src/NzbDrone.Core/Notifications/Gotify/Gotify.cs @@ -31,7 +31,7 @@ namespace NzbDrone.Core.Notifications.Gotify public override void OnHealthRestored(HealthCheck.HealthCheck previousCheck) { - _proxy.SendNotification(HEALTH_RESTORED_TITLE, $"The following issue is now resolved: {previousCheck.Message}", null); + _proxy.SendNotification(HEALTH_RESTORED_TITLE, $"The following issue is now resolved: {previousCheck.Message}", Settings); } public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) diff --git a/src/NzbDrone.Core/Notifications/NotificationFactory.cs b/src/NzbDrone.Core/Notifications/NotificationFactory.cs index f9d79940e..824aae0b5 100644 --- a/src/NzbDrone.Core/Notifications/NotificationFactory.cs +++ b/src/NzbDrone.Core/Notifications/NotificationFactory.cs @@ -79,7 +79,7 @@ namespace NzbDrone.Core.Notifications foreach (var notification in notifications) { - if (blockedNotifications.TryGetValue(notification.Definition.Id, out var notificationStatus)) + if (blockedNotifications.TryGetValue(notification.Definition.Id, out var notificationStatus) && notificationStatus.DisabledTill.HasValue) { _logger.Debug("Temporarily ignoring notification {0} till {1} due to recent failures.", notification.Definition.Name, notificationStatus.DisabledTill.Value.ToLocalTime()); continue; diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookRelease.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookRelease.cs index 295d62986..ad98e5440 100644 --- a/src/NzbDrone.Core/Notifications/Webhook/WebhookRelease.cs +++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookRelease.cs @@ -1,3 +1,6 @@ +using System; +using System.Collections.Generic; +using System.Linq; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Notifications.Webhook @@ -13,10 +16,18 @@ namespace NzbDrone.Core.Notifications.Webhook ReleaseTitle = release.Title; Indexer = release.Indexer; Size = release.Size; + Categories = release.Categories.Select(f => f.Name).ToList(); + Genres = release.Genres.ToList(); + IndexerFlags = release.IndexerFlags.Select(f => f.Name).ToHashSet(); + PublishDate = release.PublishDate; } public string ReleaseTitle { get; set; } public string Indexer { get; set; } public long? Size { get; set; } + public List<string> Categories { get; set; } + public List<string> Genres { get; set; } + public HashSet<string> IndexerFlags { get; set; } + public DateTime? PublishDate { get; set; } } } diff --git a/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs b/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs index 7d8c83d50..6f0788607 100644 --- a/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs +++ b/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs @@ -9,6 +9,7 @@ namespace NzbDrone.Core.Parser.Model { public ReleaseInfo() { + Genres = new List<string>(); IndexerFlags = new HashSet<IndexerFlag>(); Categories = new List<IndexerCategory>(); Languages = new List<string>(); diff --git a/src/NzbDrone.Core/Parser/ParseUtil.cs b/src/NzbDrone.Core/Parser/ParseUtil.cs index ea80afb20..3927adff9 100644 --- a/src/NzbDrone.Core/Parser/ParseUtil.cs +++ b/src/NzbDrone.Core/Parser/ParseUtil.cs @@ -15,6 +15,11 @@ namespace NzbDrone.Core.Parser private static string NormalizeNumber(string s, bool isInt = false) { + if (s == null) + { + return null; + } + var valStr = new string(s.Where(c => char.IsDigit(c) || c == '.' || c == ',').ToArray()); valStr = valStr.Trim().Replace("-", "0"); diff --git a/src/NzbDrone.Core/Prowlarr.Core.csproj b/src/NzbDrone.Core/Prowlarr.Core.csproj index 50fdb89e8..3f70dd577 100644 --- a/src/NzbDrone.Core/Prowlarr.Core.csproj +++ b/src/NzbDrone.Core/Prowlarr.Core.csproj @@ -5,25 +5,27 @@ <ItemGroup> <PackageReference Include="AngleSharp.Xml" Version="1.0.0" /> <PackageReference Include="Dapper" Version="2.0.151" /> - <PackageReference Include="MailKit" Version="3.6.0" /> - <PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="6.0.29" /> + <PackageReference Include="Diacritical.Net" Version="1.0.4" /> + <PackageReference Include="MailKit" Version="4.8.0" /> + <PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="6.0.35" /> <PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" /> + <PackageReference Include="Microsoft.Data.SqlClient" Version="2.1.7" /> <PackageReference Include="NLog.Targets.Syslog" Version="7.0.0" /> - <PackageReference Include="Npgsql" Version="7.0.6" /> - <PackageReference Include="Polly" Version="8.3.1" /> + <PackageReference Include="Npgsql" Version="7.0.10" /> + <PackageReference Include="Polly" Version="8.5.2" /> <PackageReference Include="Servarr.FluentMigrator.Runner" Version="3.3.2.9" /> <PackageReference Include="Servarr.FluentMigrator.Runner.Postgres" Version="3.3.2.9" /> <PackageReference Include="Servarr.FluentMigrator.Runner.SQLite" Version="3.3.2.9" /> - <PackageReference Include="System.Memory" Version="4.5.5" /> + <PackageReference Include="System.Memory" Version="4.6.3" /> <PackageReference Include="System.ServiceModel.Syndication" Version="6.0.0" /> <PackageReference Include="FluentValidation" Version="9.5.4" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> - <PackageReference Include="NLog" Version="5.2.0" /> + <PackageReference Include="NLog" Version="5.4.0" /> <PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" /> - <PackageReference Include="System.Text.Json" Version="6.0.9" /> + <PackageReference Include="System.Text.Json" Version="6.0.11" /> <PackageReference Include="MonoTorrent" Version="2.0.7" /> <PackageReference Include="YamlDotNet" Version="13.1.1" /> - <PackageReference Include="AngleSharp" Version="1.1.2" /> + <PackageReference Include="AngleSharp" Version="1.2.0" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\NzbDrone.Common\Prowlarr.Common.csproj" /> diff --git a/src/NzbDrone.Core/ThingiProvider/ProviderFactory.cs b/src/NzbDrone.Core/ThingiProvider/ProviderFactory.cs index fad090940..73e82f30a 100644 --- a/src/NzbDrone.Core/ThingiProvider/ProviderFactory.cs +++ b/src/NzbDrone.Core/ThingiProvider/ProviderFactory.cs @@ -127,10 +127,12 @@ namespace NzbDrone.Core.ThingiProvider public virtual IEnumerable<TProviderDefinition> Update(IEnumerable<TProviderDefinition> definitions) { - _providerRepository.UpdateMany(definitions.ToList()); - _eventAggregator.PublishEvent(new ProviderBulkUpdatedEvent<TProvider>(definitions)); + var providerDefinitions = definitions.ToList(); - return definitions; + _providerRepository.UpdateMany(providerDefinitions); + _eventAggregator.PublishEvent(new ProviderBulkUpdatedEvent<TProvider>(providerDefinitions)); + + return providerDefinitions; } public void Delete(int id) diff --git a/src/NzbDrone.Core/ThingiProvider/Status/ProviderStatusRepository.cs b/src/NzbDrone.Core/ThingiProvider/Status/ProviderStatusRepository.cs index 7a2d1bc78..2748d30b5 100644 --- a/src/NzbDrone.Core/ThingiProvider/Status/ProviderStatusRepository.cs +++ b/src/NzbDrone.Core/ThingiProvider/Status/ProviderStatusRepository.cs @@ -8,6 +8,7 @@ namespace NzbDrone.Core.ThingiProvider.Status where TModel : ProviderStatusBase, new() { TModel FindByProviderId(int providerId); + void DeleteByProviderId(int providerId); } public class ProviderStatusRepository<TModel> : BasicRepository<TModel>, IProviderStatusRepository<TModel> @@ -22,5 +23,10 @@ namespace NzbDrone.Core.ThingiProvider.Status { return Query(x => x.ProviderId == providerId).SingleOrDefault(); } + + public void DeleteByProviderId(int providerId) + { + Delete(c => c.ProviderId == providerId); + } } } diff --git a/src/NzbDrone.Core/ThingiProvider/Status/ProviderStatusServiceBase.cs b/src/NzbDrone.Core/ThingiProvider/Status/ProviderStatusServiceBase.cs index 6279c6e35..dc9f6e807 100644 --- a/src/NzbDrone.Core/ThingiProvider/Status/ProviderStatusServiceBase.cs +++ b/src/NzbDrone.Core/ThingiProvider/Status/ProviderStatusServiceBase.cs @@ -151,12 +151,7 @@ namespace NzbDrone.Core.ThingiProvider.Status public virtual void HandleAsync(ProviderDeletedEvent<TProvider> message) { - var providerStatus = _providerStatusRepository.FindByProviderId(message.ProviderId); - - if (providerStatus != null) - { - _providerStatusRepository.Delete(providerStatus); - } + _providerStatusRepository.DeleteByProviderId(message.ProviderId); } } } diff --git a/src/NzbDrone.Core/Update/Commands/ApplicationCheckUpdateCommand.cs b/src/NzbDrone.Core/Update/Commands/ApplicationUpdateCheckCommand.cs similarity index 65% rename from src/NzbDrone.Core/Update/Commands/ApplicationCheckUpdateCommand.cs rename to src/NzbDrone.Core/Update/Commands/ApplicationUpdateCheckCommand.cs index 6987af3fa..fa2cfbf41 100644 --- a/src/NzbDrone.Core/Update/Commands/ApplicationCheckUpdateCommand.cs +++ b/src/NzbDrone.Core/Update/Commands/ApplicationUpdateCheckCommand.cs @@ -2,10 +2,12 @@ using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Update.Commands { - public class ApplicationCheckUpdateCommand : Command + public class ApplicationUpdateCheckCommand : Command { public override bool SendUpdatesToClient => true; public override string CompletionMessage => null; + + public bool InstallMajorUpdate { get; set; } } } diff --git a/src/NzbDrone.Core/Update/Commands/ApplicationUpdateCommand.cs b/src/NzbDrone.Core/Update/Commands/ApplicationUpdateCommand.cs index 59a827a0b..6980af708 100644 --- a/src/NzbDrone.Core/Update/Commands/ApplicationUpdateCommand.cs +++ b/src/NzbDrone.Core/Update/Commands/ApplicationUpdateCommand.cs @@ -4,6 +4,7 @@ namespace NzbDrone.Core.Update.Commands { public class ApplicationUpdateCommand : Command { + public bool InstallMajorUpdate { get; set; } public override bool SendUpdatesToClient => true; public override bool IsExclusive => true; } diff --git a/src/NzbDrone.Core/Update/InstallUpdateService.cs b/src/NzbDrone.Core/Update/InstallUpdateService.cs index f1d299c11..793e1a8ac 100644 --- a/src/NzbDrone.Core/Update/InstallUpdateService.cs +++ b/src/NzbDrone.Core/Update/InstallUpdateService.cs @@ -20,7 +20,7 @@ using NzbDrone.Core.Update.Commands; namespace NzbDrone.Core.Update { - public class InstallUpdateService : IExecute<ApplicationCheckUpdateCommand>, IExecute<ApplicationUpdateCommand>, IHandle<ApplicationStartingEvent> + public class InstallUpdateService : IExecute<ApplicationUpdateCommand>, IExecute<ApplicationUpdateCheckCommand>, IHandle<ApplicationStartingEvent> { private readonly ICheckUpdateService _checkUpdateService; private readonly Logger _logger; @@ -83,7 +83,7 @@ namespace NzbDrone.Core.Update { EnsureAppDataSafety(); - if (OsInfo.IsWindows || _configFileProvider.UpdateMechanism != UpdateMechanism.Script) + if (_configFileProvider.UpdateMechanism != UpdateMechanism.Script) { var startupFolder = _appFolderInfo.StartUpFolder; var uiFolder = Path.Combine(startupFolder, "UI"); @@ -143,7 +143,7 @@ namespace NzbDrone.Core.Update _backupService.Backup(BackupType.Update); - if (OsInfo.IsNotWindows && _configFileProvider.UpdateMechanism == UpdateMechanism.Script) + if (_configFileProvider.UpdateMechanism == UpdateMechanism.Script) { InstallUpdateWithScript(updateSandboxFolder); return true; @@ -231,7 +231,7 @@ namespace NzbDrone.Core.Update } } - private UpdatePackage GetUpdatePackage(CommandTrigger updateTrigger) + private UpdatePackage GetUpdatePackage(CommandTrigger updateTrigger, bool installMajorUpdate) { _logger.ProgressDebug("Checking for updates"); @@ -243,18 +243,24 @@ namespace NzbDrone.Core.Update return null; } - if (_osInfo.IsDocker) + if (latestAvailable.Version.Major > BuildInfo.Version.Major && !installMajorUpdate) { - _logger.ProgressDebug("Updating is disabled inside a docker container. Please update the container image."); + _logger.ProgressInfo("Unable to install major update, please update update manually from System: Updates"); return null; } - if (OsInfo.IsNotWindows && !_configFileProvider.UpdateAutomatically && updateTrigger != CommandTrigger.Manual) + if (!_configFileProvider.UpdateAutomatically && updateTrigger != CommandTrigger.Manual) { _logger.ProgressDebug("Auto-update not enabled, not installing available update."); return null; } + if (_configFileProvider.UpdateMechanism == UpdateMechanism.BuiltIn && _deploymentInfoProvider.PackageUpdateMechanism == UpdateMechanism.Docker) + { + _logger.ProgressDebug("Built-In updater disabled inside a docker container. Please update the container image."); + return null; + } + // Safety net, ConfigureUpdateMechanism should take care of invalid settings if (_configFileProvider.UpdateMechanism == UpdateMechanism.BuiltIn && _deploymentInfoProvider.IsExternalUpdateMechanism) { @@ -270,9 +276,9 @@ namespace NzbDrone.Core.Update return latestAvailable; } - public void Execute(ApplicationCheckUpdateCommand message) + public void Execute(ApplicationUpdateCheckCommand message) { - if (GetUpdatePackage(message.Trigger) != null) + if (GetUpdatePackage(message.Trigger, true) != null) { _commandQueueManager.Push(new ApplicationUpdateCommand(), trigger: message.Trigger); } @@ -280,7 +286,7 @@ namespace NzbDrone.Core.Update public void Execute(ApplicationUpdateCommand message) { - var latestAvailable = GetUpdatePackage(message.Trigger); + var latestAvailable = GetUpdatePackage(message.Trigger, message.InstallMajorUpdate); if (latestAvailable != null) { diff --git a/src/NzbDrone.Core/Update/UpdatePackageProvider.cs b/src/NzbDrone.Core/Update/UpdatePackageProvider.cs index 17743c8b0..58d9872cc 100644 --- a/src/NzbDrone.Core/Update/UpdatePackageProvider.cs +++ b/src/NzbDrone.Core/Update/UpdatePackageProvider.cs @@ -47,6 +47,7 @@ namespace NzbDrone.Core.Update .AddQueryParam("runtime", "netcore") .AddQueryParam("runtimeVer", _platformInfo.Version) .AddQueryParam("dbType", _mainDatabase.DatabaseType) + .AddQueryParam("includeMajorVersion", true) .SetSegment("branch", branch); if (_analyticsService.IsEnabled) diff --git a/src/NzbDrone.Core/Validation/IpValidation.cs b/src/NzbDrone.Core/Validation/IpValidation.cs index eb5863caa..f4afa1f66 100644 --- a/src/NzbDrone.Core/Validation/IpValidation.cs +++ b/src/NzbDrone.Core/Validation/IpValidation.cs @@ -1,5 +1,4 @@ using FluentValidation; -using FluentValidation.Validators; using NzbDrone.Common.Extensions; namespace NzbDrone.Core.Validation @@ -10,10 +9,5 @@ namespace NzbDrone.Core.Validation { return ruleBuilder.Must(x => x.IsValidIpAddress()).WithMessage("Must contain wildcard (*) or a valid IP Address"); } - - public static IRuleBuilderOptions<T, string> NotListenAllIp4Address<T>(this IRuleBuilder<T, string> ruleBuilder) - { - return ruleBuilder.SetValidator(new RegularExpressionValidator(@"^(?!0\.0\.0\.0)")).WithMessage("Use * instead of 0.0.0.0"); - } } } diff --git a/src/NzbDrone.Host/Prowlarr.Host.csproj b/src/NzbDrone.Host/Prowlarr.Host.csproj index 4eb549ced..12bd5168e 100644 --- a/src/NzbDrone.Host/Prowlarr.Host.csproj +++ b/src/NzbDrone.Host/Prowlarr.Host.csproj @@ -4,10 +4,10 @@ <OutputType>Library</OutputType> </PropertyGroup> <ItemGroup> - <PackageReference Include="NLog.Extensions.Logging" Version="5.3.0" /> - <PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" /> - <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.2" /> - <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" /> + <PackageReference Include="NLog.Extensions.Logging" Version="5.4.0" /> + <PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.1" /> + <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.3" /> + <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="7.3.2" /> <PackageReference Include="DryIoc.dll" Version="5.4.3" /> <PackageReference Include="DryIoc.Microsoft.DependencyInjection" Version="6.2.0" /> </ItemGroup> diff --git a/src/NzbDrone.Host/Startup.cs b/src/NzbDrone.Host/Startup.cs index 10ea61e15..5a1dc92d9 100644 --- a/src/NzbDrone.Host/Startup.cs +++ b/src/NzbDrone.Host/Startup.cs @@ -135,7 +135,7 @@ namespace NzbDrone.Host Name = "apikey", Type = SecuritySchemeType.ApiKey, Scheme = "apiKey", - Description = "Apikey passed as header", + Description = "Apikey passed as query parameter", In = ParameterLocation.Query, Reference = new OpenApiReference { diff --git a/src/NzbDrone.Integration.Test/ApiTests/CommandFixture.cs b/src/NzbDrone.Integration.Test/ApiTests/CommandFixture.cs index 91d0962ab..f8a8ccd51 100644 --- a/src/NzbDrone.Integration.Test/ApiTests/CommandFixture.cs +++ b/src/NzbDrone.Integration.Test/ApiTests/CommandFixture.cs @@ -10,7 +10,7 @@ namespace NzbDrone.Integration.Test.ApiTests [Test] public void should_be_able_to_run_update_check() { - var response = Commands.Post(new SimpleCommandResource { Name = "applicationcheckupdate" }); + var response = Commands.Post(new SimpleCommandResource { Name = "applicationupdatecheck" }); response.Id.Should().NotBe(0); } diff --git a/src/NzbDrone.Integration.Test/Prowlarr.Integration.Test.csproj b/src/NzbDrone.Integration.Test/Prowlarr.Integration.Test.csproj index a6f817f63..a4c142248 100644 --- a/src/NzbDrone.Integration.Test/Prowlarr.Integration.Test.csproj +++ b/src/NzbDrone.Integration.Test/Prowlarr.Integration.Test.csproj @@ -4,7 +4,7 @@ <OutputType>Library</OutputType> </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.29" /> + <PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.35" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\NzbDrone.Test.Common\Prowlarr.Test.Common.csproj" /> diff --git a/src/NzbDrone.Test.Common/Prowlarr.Test.Common.csproj b/src/NzbDrone.Test.Common/Prowlarr.Test.Common.csproj index 59dcf031a..b9ccacd9e 100644 --- a/src/NzbDrone.Test.Common/Prowlarr.Test.Common.csproj +++ b/src/NzbDrone.Test.Common/Prowlarr.Test.Common.csproj @@ -6,7 +6,7 @@ <PackageReference Include="FluentAssertions" Version="6.11.0" /> <PackageReference Include="FluentValidation" Version="9.5.4" /> <PackageReference Include="Moq" Version="4.17.2" /> - <PackageReference Include="NLog" Version="5.2.0" /> + <PackageReference Include="NLog" Version="5.4.0" /> <PackageReference Include="NUnit" Version="3.14.0" /> <PackageReference Include="RestSharp" Version="106.15.0" /> <PackageReference Include="RestSharp.Serializers.SystemTextJson" Version="106.15.0" /> diff --git a/src/NzbDrone.Update/Prowlarr.Update.csproj b/src/NzbDrone.Update/Prowlarr.Update.csproj index 2d2812f00..09b95e2f8 100644 --- a/src/NzbDrone.Update/Prowlarr.Update.csproj +++ b/src/NzbDrone.Update/Prowlarr.Update.csproj @@ -6,7 +6,7 @@ <ItemGroup> <PackageReference Include="DryIoc.dll" Version="5.4.3" /> <PackageReference Include="DryIoc.Microsoft.DependencyInjection" Version="6.2.0" /> - <PackageReference Include="NLog" Version="5.2.0" /> + <PackageReference Include="NLog" Version="5.4.0" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\NzbDrone.Common\Prowlarr.Common.csproj" /> diff --git a/src/NzbDrone.Windows/Prowlarr.Windows.csproj b/src/NzbDrone.Windows/Prowlarr.Windows.csproj index e4407e1cd..4a9f864e5 100644 --- a/src/NzbDrone.Windows/Prowlarr.Windows.csproj +++ b/src/NzbDrone.Windows/Prowlarr.Windows.csproj @@ -4,7 +4,7 @@ <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> </PropertyGroup> <ItemGroup> - <PackageReference Include="NLog" Version="5.2.0" /> + <PackageReference Include="NLog" Version="5.4.0" /> <PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" /> </ItemGroup> <ItemGroup> diff --git a/src/Prowlarr.Api.V1/Applications/ApplicationController.cs b/src/Prowlarr.Api.V1/Applications/ApplicationController.cs index c3819d8f9..a63beedcf 100644 --- a/src/Prowlarr.Api.V1/Applications/ApplicationController.cs +++ b/src/Prowlarr.Api.V1/Applications/ApplicationController.cs @@ -1,4 +1,5 @@ using NzbDrone.Core.Applications; +using NzbDrone.SignalR; using Prowlarr.Http; namespace Prowlarr.Api.V1.Applications @@ -9,8 +10,8 @@ namespace Prowlarr.Api.V1.Applications public static readonly ApplicationResourceMapper ResourceMapper = new (); public static readonly ApplicationBulkResourceMapper BulkResourceMapper = new (); - public ApplicationController(ApplicationFactory applicationsFactory) - : base(applicationsFactory, "applications", ResourceMapper, BulkResourceMapper) + public ApplicationController(IBroadcastSignalRMessage signalRBroadcaster, ApplicationFactory applicationsFactory) + : base(signalRBroadcaster, applicationsFactory, "applications", ResourceMapper, BulkResourceMapper) { } } diff --git a/src/Prowlarr.Api.V1/Applications/ApplicationResource.cs b/src/Prowlarr.Api.V1/Applications/ApplicationResource.cs index 100d9083a..417ba238a 100644 --- a/src/Prowlarr.Api.V1/Applications/ApplicationResource.cs +++ b/src/Prowlarr.Api.V1/Applications/ApplicationResource.cs @@ -1,10 +1,15 @@ using NzbDrone.Core.Applications; +using Swashbuckle.AspNetCore.Annotations; namespace Prowlarr.Api.V1.Applications { public class ApplicationResource : ProviderResource<ApplicationResource> { public ApplicationSyncLevel SyncLevel { get; set; } + + [SwaggerIgnore] + public bool Enable { get; set; } + public string TestCommand { get; set; } } @@ -20,6 +25,7 @@ namespace Prowlarr.Api.V1.Applications var resource = base.ToResource(definition); resource.SyncLevel = definition.SyncLevel; + resource.Enable = definition.Enable; return resource; } diff --git a/src/Prowlarr.Api.V1/Commands/CommandController.cs b/src/Prowlarr.Api.V1/Commands/CommandController.cs index f74192f55..714685b22 100644 --- a/src/Prowlarr.Api.V1/Commands/CommandController.cs +++ b/src/Prowlarr.Api.V1/Commands/CommandController.cs @@ -61,9 +61,8 @@ namespace Prowlarr.Api.V1.Commands using var reader = new StreamReader(Request.Body); var body = reader.ReadToEnd(); - dynamic command = STJson.Deserialize(body, commandType); + var command = STJson.Deserialize(body, commandType) as Command; - command.Trigger = CommandTrigger.Manual; command.SuppressMessages = !command.SendUpdatesToClient; command.SendUpdatesToClient = true; command.ClientUserAgent = Request.Headers["User-Agent"]; diff --git a/src/Prowlarr.Api.V1/Config/HostConfigController.cs b/src/Prowlarr.Api.V1/Config/HostConfigController.cs index 1600c6664..a844f8d2e 100644 --- a/src/Prowlarr.Api.V1/Config/HostConfigController.cs +++ b/src/Prowlarr.Api.V1/Config/HostConfigController.cs @@ -34,7 +34,6 @@ namespace Prowlarr.Api.V1.Config SharedValidator.RuleFor(c => c.BindAddress) .ValidIpAddress() - .NotListenAllIp4Address() .When(c => c.BindAddress != "*" && c.BindAddress != "localhost"); SharedValidator.RuleFor(c => c.Port).ValidPort(); @@ -61,6 +60,8 @@ namespace Prowlarr.Api.V1.Config .Must((resource, path) => IsValidSslCertificate(resource)).WithMessage("Invalid SSL certificate file or password") .When(c => c.EnableSsl); + SharedValidator.RuleFor(c => c.LogSizeLimit).InclusiveBetween(1, 10); + SharedValidator.RuleFor(c => c.Branch).NotEmpty().WithMessage("Branch name is required, 'master' is the default"); SharedValidator.RuleFor(c => c.UpdateScriptPath).IsValidPath().When(c => c.UpdateMechanism == UpdateMechanism.Script); diff --git a/src/Prowlarr.Api.V1/Config/HostConfigResource.cs b/src/Prowlarr.Api.V1/Config/HostConfigResource.cs index 425453913..4bd0cd10a 100644 --- a/src/Prowlarr.Api.V1/Config/HostConfigResource.cs +++ b/src/Prowlarr.Api.V1/Config/HostConfigResource.cs @@ -21,6 +21,7 @@ namespace Prowlarr.Api.V1.Config public string Password { get; set; } public string PasswordConfirmation { get; set; } public string LogLevel { get; set; } + public int LogSizeLimit { get; set; } public string ConsoleLogLevel { get; set; } public string Branch { get; set; } public string ApiKey { get; set; } @@ -45,6 +46,7 @@ namespace Prowlarr.Api.V1.Config public int BackupInterval { get; set; } public int BackupRetention { get; set; } public int HistoryCleanupDays { get; set; } + public bool TrustCgnatIpAddresses { get; set; } } public static class HostConfigResourceMapper @@ -66,6 +68,7 @@ namespace Prowlarr.Api.V1.Config //Username //Password LogLevel = model.LogLevel, + LogSizeLimit = model.LogSizeLimit, ConsoleLogLevel = model.ConsoleLogLevel, Branch = model.Branch, ApiKey = model.ApiKey, diff --git a/src/Prowlarr.Api.V1/DownloadClient/DownloadClientController.cs b/src/Prowlarr.Api.V1/DownloadClient/DownloadClientController.cs index 5dd43ea7d..347c7e9c6 100644 --- a/src/Prowlarr.Api.V1/DownloadClient/DownloadClientController.cs +++ b/src/Prowlarr.Api.V1/DownloadClient/DownloadClientController.cs @@ -1,4 +1,6 @@ +using FluentValidation; using NzbDrone.Core.Download; +using NzbDrone.SignalR; using Prowlarr.Http; namespace Prowlarr.Api.V1.DownloadClient @@ -9,9 +11,10 @@ namespace Prowlarr.Api.V1.DownloadClient public static readonly DownloadClientResourceMapper ResourceMapper = new (); public static readonly DownloadClientBulkResourceMapper BulkResourceMapper = new (); - public DownloadClientController(IDownloadClientFactory downloadClientFactory) - : base(downloadClientFactory, "downloadclient", ResourceMapper, BulkResourceMapper) + public DownloadClientController(IBroadcastSignalRMessage signalRBroadcaster, IDownloadClientFactory downloadClientFactory) + : base(signalRBroadcaster, downloadClientFactory, "downloadclient", ResourceMapper, BulkResourceMapper) { + SharedValidator.RuleFor(c => c.Priority).InclusiveBetween(1, 50); } } } diff --git a/src/Prowlarr.Api.V1/Health/HealthResource.cs b/src/Prowlarr.Api.V1/Health/HealthResource.cs index 1e2c8ffa5..3f357285d 100644 --- a/src/Prowlarr.Api.V1/Health/HealthResource.cs +++ b/src/Prowlarr.Api.V1/Health/HealthResource.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using NzbDrone.Common.Http; using NzbDrone.Core.HealthCheck; using Prowlarr.Http.REST; @@ -11,7 +10,7 @@ namespace Prowlarr.Api.V1.Health public string Source { get; set; } public HealthCheckResult Type { get; set; } public string Message { get; set; } - public HttpUri WikiUrl { get; set; } + public string WikiUrl { get; set; } } public static class HealthResourceMapper @@ -29,7 +28,7 @@ namespace Prowlarr.Api.V1.Health Source = model.Source.Name, Type = model.Type, Message = model.Message, - WikiUrl = model.WikiUrl + WikiUrl = model.WikiUrl.FullUri }; } diff --git a/src/Prowlarr.Api.V1/History/HistoryController.cs b/src/Prowlarr.Api.V1/History/HistoryController.cs index 35b9be44d..a744ffc2f 100644 --- a/src/Prowlarr.Api.V1/History/HistoryController.cs +++ b/src/Prowlarr.Api.V1/History/HistoryController.cs @@ -25,7 +25,13 @@ namespace Prowlarr.Api.V1.History public PagingResource<HistoryResource> GetHistory([FromQuery] PagingRequestResource paging, [FromQuery(Name = "eventType")] int[] eventTypes, bool? successful, string downloadId, [FromQuery] int[] indexerIds = null) { var pagingResource = new PagingResource<HistoryResource>(paging); - var pagingSpec = pagingResource.MapToPagingSpec<HistoryResource, NzbDrone.Core.History.History>("date", SortDirection.Descending); + var pagingSpec = pagingResource.MapToPagingSpec<HistoryResource, NzbDrone.Core.History.History>( + new HashSet<string>(StringComparer.OrdinalIgnoreCase) + { + "date" + }, + "date", + SortDirection.Descending); if (eventTypes != null && eventTypes.Any()) { diff --git a/src/Prowlarr.Api.V1/IndexerProxies/IndexerProxyController.cs b/src/Prowlarr.Api.V1/IndexerProxies/IndexerProxyController.cs index ba6cbfbe7..d1be85292 100644 --- a/src/Prowlarr.Api.V1/IndexerProxies/IndexerProxyController.cs +++ b/src/Prowlarr.Api.V1/IndexerProxies/IndexerProxyController.cs @@ -1,6 +1,7 @@ using System; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.IndexerProxies; +using NzbDrone.SignalR; using Prowlarr.Http; namespace Prowlarr.Api.V1.IndexerProxies @@ -11,8 +12,8 @@ namespace Prowlarr.Api.V1.IndexerProxies public static readonly IndexerProxyResourceMapper ResourceMapper = new (); public static readonly IndexerProxyBulkResourceMapper BulkResourceMapper = new (); - public IndexerProxyController(IndexerProxyFactory notificationFactory) - : base(notificationFactory, "indexerProxy", ResourceMapper, BulkResourceMapper) + public IndexerProxyController(IBroadcastSignalRMessage signalRBroadcaster, IndexerProxyFactory notificationFactory) + : base(signalRBroadcaster, notificationFactory, "indexerProxy", ResourceMapper, BulkResourceMapper) { } diff --git a/src/Prowlarr.Api.V1/Indexers/IndexerBulkResource.cs b/src/Prowlarr.Api.V1/Indexers/IndexerBulkResource.cs index 7f3d281f0..7434622f2 100644 --- a/src/Prowlarr.Api.V1/Indexers/IndexerBulkResource.cs +++ b/src/Prowlarr.Api.V1/Indexers/IndexerBulkResource.cs @@ -12,6 +12,7 @@ namespace Prowlarr.Api.V1.Indexers public double? SeedRatio { get; set; } public int? SeedTime { get; set; } public int? PackSeedTime { get; set; } + public bool? PreferMagnetUrl { get; set; } } public class IndexerBulkResourceMapper : ProviderBulkResourceMapper<IndexerBulkResource, IndexerDefinition> @@ -35,6 +36,7 @@ namespace Prowlarr.Api.V1.Indexers ((ITorrentIndexerSettings)existing.Settings).TorrentBaseSettings.SeedRatio = resource.SeedRatio ?? ((ITorrentIndexerSettings)existing.Settings).TorrentBaseSettings.SeedRatio; ((ITorrentIndexerSettings)existing.Settings).TorrentBaseSettings.SeedTime = resource.SeedTime ?? ((ITorrentIndexerSettings)existing.Settings).TorrentBaseSettings.SeedTime; ((ITorrentIndexerSettings)existing.Settings).TorrentBaseSettings.PackSeedTime = resource.PackSeedTime ?? ((ITorrentIndexerSettings)existing.Settings).TorrentBaseSettings.PackSeedTime; + ((ITorrentIndexerSettings)existing.Settings).TorrentBaseSettings.PreferMagnetUrl = resource.PreferMagnetUrl ?? ((ITorrentIndexerSettings)existing.Settings).TorrentBaseSettings.PreferMagnetUrl; } }); diff --git a/src/Prowlarr.Api.V1/Indexers/IndexerController.cs b/src/Prowlarr.Api.V1/Indexers/IndexerController.cs index 972ae4426..07955299d 100644 --- a/src/Prowlarr.Api.V1/Indexers/IndexerController.cs +++ b/src/Prowlarr.Api.V1/Indexers/IndexerController.cs @@ -1,5 +1,7 @@ +using FluentValidation; using NzbDrone.Core.Indexers; using NzbDrone.Core.Validation; +using NzbDrone.SignalR; using Prowlarr.Http; namespace Prowlarr.Api.V1.Indexers @@ -7,16 +9,19 @@ namespace Prowlarr.Api.V1.Indexers [V1ApiController] public class IndexerController : ProviderControllerBase<IndexerResource, IndexerBulkResource, IIndexer, IndexerDefinition> { - public IndexerController(IndexerFactory indexerFactory, + public IndexerController(IBroadcastSignalRMessage signalRBroadcaster, + IndexerFactory indexerFactory, IndexerResourceMapper resourceMapper, IndexerBulkResourceMapper bulkResourceMapper, AppProfileExistsValidator appProfileExistsValidator, DownloadClientExistsValidator downloadClientExistsValidator) - : base(indexerFactory, "indexer", resourceMapper, bulkResourceMapper) + : base(signalRBroadcaster, indexerFactory, "indexer", resourceMapper, bulkResourceMapper) { - Http.Validation.RuleBuilderExtensions.ValidId(SharedValidator.RuleFor(s => s.AppProfileId)); + SharedValidator.RuleFor(c => c.AppProfileId).Cascade(CascadeMode.Stop) + .ValidId() + .SetValidator(appProfileExistsValidator); - SharedValidator.RuleFor(c => c.AppProfileId).SetValidator(appProfileExistsValidator); + SharedValidator.RuleFor(c => c.Priority).InclusiveBetween(1, 50); SharedValidator.RuleFor(c => c.DownloadClientId).SetValidator(downloadClientExistsValidator); } } diff --git a/src/Prowlarr.Api.V1/Indexers/IndexerResource.cs b/src/Prowlarr.Api.V1/Indexers/IndexerResource.cs index e82ad2ab5..31624dbf2 100644 --- a/src/Prowlarr.Api.V1/Indexers/IndexerResource.cs +++ b/src/Prowlarr.Api.V1/Indexers/IndexerResource.cs @@ -60,7 +60,7 @@ namespace Prowlarr.Api.V1.Indexers if (definition.Implementation == nameof(Cardigann)) { - var extraFields = definition.ExtraFields?.Select(MapField).ToList() ?? new List<Field>(); + var extraFields = definition.ExtraFields?.Select((field, i) => MapCardigannField(definition, field, i)).ToList() ?? new List<Field>(); resource.Fields.AddRange(extraFields); @@ -119,20 +119,29 @@ namespace Prowlarr.Api.V1.Indexers var settings = (CardigannSettings)definition.Settings; - var cardigannDefinition = _definitionService.GetCachedDefinition(settings.DefinitionFile); - - foreach (var field in resource.Fields) + if (settings.DefinitionFile.IsNotNullOrWhiteSpace()) { - if (!standardFields.Contains(field.Name)) + var cardigannDefinition = _definitionService.GetCachedDefinition(settings.DefinitionFile); + + foreach (var field in resource.Fields) { - if (field.Name == "cardigannCaptcha") + if (!standardFields.Contains(field.Name)) { - settings.ExtraFieldData["CAPTCHA"] = field.Value?.ToString() ?? string.Empty; - } - else - { - var cardigannSetting = cardigannDefinition.Settings.FirstOrDefault(x => x.Name == field.Name); - settings.ExtraFieldData[field.Name] = MapValue(cardigannSetting, field.Value); + if (field.Name == "cardigannCaptcha") + { + settings.ExtraFieldData["CAPTCHA"] = field.Value?.ToString() ?? string.Empty; + } + else + { + var cardigannSetting = cardigannDefinition.Settings.FirstOrDefault(x => x.Name == field.Name); + + if (cardigannSetting == null) + { + throw new ArgumentOutOfRangeException(field.Name, "Unknown Cardigann setting."); + } + + settings.ExtraFieldData[field.Name] = MapValue(cardigannSetting, field.Value); + } } } } @@ -160,7 +169,7 @@ namespace Prowlarr.Api.V1.Indexers }; } - private Field MapField(SettingsField setting, int order) + private Field MapCardigannField(IndexerDefinition definition, SettingsField setting, int order) { var field = new Field { @@ -185,6 +194,34 @@ namespace Prowlarr.Api.V1.Indexers { field.Value = bool.TryParse(setting.Default, out var value) && value; } + else if (setting.Type is "info_cookie" or "info_flaresolverr" or "info_useragent" or "info_category_8000") + { + field.Type = "info"; + + switch (setting.Type) + { + case "info_cookie": + field.Label = "How to get the Cookie"; + field.Value = "<ol><li>Login to this tracker with your browser</li><li>If present in the login page, ensure you have the <b>Remember me</b> ticked and the <b>Log Me Out if IP Changes</b> unticked when you login</li><li>Navigate to the web site's torrent search page to view the list of available torrents for download</li><li>Open the <b>DevTools</b> panel by pressing <b>F12</b></li><li>Select the <b>Network</b> tab</li><li>Click on the <b>Doc</b> button (Chrome Browser) or <b>HTML</b> button (FireFox)</li><li>Refresh the page by pressing <b>F5</b></li><li>Click on the first row entry</li><li>Select the <b>Headers</b> tab on the Right panel</li><li>Find <b>'cookie:'</b> in the <b>Request Headers</b> section</li><li><b>Select</b> and <b>Copy</b> the whole cookie string <i>(everything after 'cookie: ')</i> and <b>Paste</b> here.</li></ol>"; + field.HelpLink = "https://wiki.servarr.com/useful-tools#finding-cookies"; + break; + case "info_useragent": + field.Label = "How to get the User-Agent"; + field.Value = "<ol><li>From the same place you fetched the cookie,</li><li>Find <b>'user-agent:'</b> in the <b>Request Headers</b> section</li><li><b>Select</b> and <b>Copy</b> the whole user-agent string <i>(everything after 'user-agent: ')</i> and <b>Paste</b> here.</li></ol>"; + field.HelpLink = "https://wiki.servarr.com/useful-tools#finding-cookies"; + break; + case "info_flaresolverr": + field.Label = "FlareSolverr Info"; + field.Value = "This site may use Cloudflare DDoS Protection, therefore Prowlarr requires <a href=\"https://wiki.servarr.com/prowlarr/faq#can-i-use-flaresolverr-indexers\" target=\"_blank\" rel=\"noreferrer\">FlareSolverr</a> to access it."; + field.HelpLink = "https://wiki.servarr.com/prowlarr/faq#can-i-use-flaresolverr-indexers"; + break; + case "info_category_8000": + field.Label = $"About {definition.Name} Categories"; + field.Value = $"{definition.Name} does not return categories in its search results. To sync to your apps, include 8000(Other) in your Apps' Sync Categories."; + field.HelpLink = "https://wiki.servarr.com/prowlarr/faq#prowlarr-will-not-sync-x-indexer-to-app"; + break; + } + } else { field.Value = setting.Default; diff --git a/src/Prowlarr.Api.V1/Indexers/NewznabController.cs b/src/Prowlarr.Api.V1/Indexers/NewznabController.cs index 4ee129caf..ae07a7ba7 100644 --- a/src/Prowlarr.Api.V1/Indexers/NewznabController.cs +++ b/src/Prowlarr.Api.V1/Indexers/NewznabController.cs @@ -198,7 +198,9 @@ namespace NzbDrone.Api.V1.Indexers } } - return CreateResponse(results.ToXml(indexer.Protocol)); + var preferMagnetUrl = indexer.Protocol == DownloadProtocol.Torrent && indexerDef.Settings is ITorrentIndexerSettings torrentIndexerSettings && (torrentIndexerSettings.TorrentBaseSettings?.PreferMagnetUrl ?? false); + + return CreateResponse(results.ToXml(indexer.Protocol, preferMagnetUrl)); default: return CreateResponse(CreateErrorXML(202, $"No such function ({requestType})"), statusCode: StatusCodes.Status400BadRequest); } @@ -253,20 +255,25 @@ namespace NzbDrone.Api.V1.Indexers var source = Request.GetSource(); var host = Request.GetHostName(); - var unprotectedlLink = _downloadMappingService.ConvertToNormalLink(link); + var unprotectedLink = _downloadMappingService.ConvertToNormalLink(link); + + if (unprotectedLink.IsNullOrWhiteSpace()) + { + throw new BadRequestException("Failed to normalize provided link"); + } // If Indexer is set to download via Redirect then just redirect to the link if (indexer.SupportsRedirect && indexerDef.Redirect) { - _downloadService.RecordRedirect(unprotectedlLink, id, source, host, file); - return RedirectPermanent(unprotectedlLink); + _downloadService.RecordRedirect(unprotectedLink, id, source, host, file); + return RedirectPermanent(unprotectedLink); } byte[] downloadBytes; try { - downloadBytes = await _downloadService.DownloadReport(unprotectedlLink, id, source, host, file); + downloadBytes = await _downloadService.DownloadReport(unprotectedLink, id, source, host, file); } catch (ReleaseUnavailableException ex) { @@ -334,7 +341,7 @@ namespace NzbDrone.Api.V1.Indexers { var blockedIndexers = _indexerStatusService.GetBlockedProviders().ToDictionary(v => v.ProviderId, v => v); - return blockedIndexers.TryGetValue(indexer.Definition.Id, out var blockedIndexerStatus) ? blockedIndexerStatus : null; + return blockedIndexers.GetValueOrDefault(indexer.Definition.Id); } private void AddRetryAfterHeader(int retryAfterSeconds) diff --git a/src/Prowlarr.Api.V1/Logs/LogController.cs b/src/Prowlarr.Api.V1/Logs/LogController.cs index b2886d2f1..18fad89e8 100644 --- a/src/Prowlarr.Api.V1/Logs/LogController.cs +++ b/src/Prowlarr.Api.V1/Logs/LogController.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; @@ -29,7 +31,11 @@ namespace Prowlarr.Api.V1.Logs } var pagingResource = new PagingResource<LogResource>(paging); - var pageSpec = pagingResource.MapToPagingSpec<LogResource, Log>(); + var pageSpec = pagingResource.MapToPagingSpec<LogResource, Log>(new HashSet<string>(StringComparer.OrdinalIgnoreCase) + { + "id", + "time" + }); if (pageSpec.SortKey == "time") { diff --git a/src/Prowlarr.Api.V1/Notifications/NotificationController.cs b/src/Prowlarr.Api.V1/Notifications/NotificationController.cs index b6aa8d99e..1520a8dd4 100644 --- a/src/Prowlarr.Api.V1/Notifications/NotificationController.cs +++ b/src/Prowlarr.Api.V1/Notifications/NotificationController.cs @@ -1,6 +1,7 @@ using System; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Notifications; +using NzbDrone.SignalR; using Prowlarr.Http; namespace Prowlarr.Api.V1.Notifications @@ -11,8 +12,8 @@ namespace Prowlarr.Api.V1.Notifications public static readonly NotificationResourceMapper ResourceMapper = new (); public static readonly NotificationBulkResourceMapper BulkResourceMapper = new (); - public NotificationController(NotificationFactory notificationFactory) - : base(notificationFactory, "notification", ResourceMapper, BulkResourceMapper) + public NotificationController(IBroadcastSignalRMessage signalRBroadcaster, NotificationFactory notificationFactory) + : base(signalRBroadcaster, notificationFactory, "notification", ResourceMapper, BulkResourceMapper) { } diff --git a/src/Prowlarr.Api.V1/ProviderControllerBase.cs b/src/Prowlarr.Api.V1/ProviderControllerBase.cs index 324a50972..725f6e58b 100644 --- a/src/Prowlarr.Api.V1/ProviderControllerBase.cs +++ b/src/Prowlarr.Api.V1/ProviderControllerBase.cs @@ -3,14 +3,22 @@ using System.Linq; using FluentValidation; using FluentValidation.Results; using Microsoft.AspNetCore.Mvc; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Datastore.Events; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.ThingiProvider; +using NzbDrone.Core.ThingiProvider.Events; using NzbDrone.Core.Validation; using NzbDrone.Http.REST.Attributes; +using NzbDrone.SignalR; using Prowlarr.Http.REST; namespace Prowlarr.Api.V1 { - public abstract class ProviderControllerBase<TProviderResource, TBulkProviderResource, TProvider, TProviderDefinition> : RestController<TProviderResource> + public abstract class ProviderControllerBase<TProviderResource, TBulkProviderResource, TProvider, TProviderDefinition> : RestControllerWithSignalR<TProviderResource, TProviderDefinition>, + IHandle<ProviderAddedEvent<TProvider>>, + IHandle<ProviderUpdatedEvent<TProvider>>, + IHandle<ProviderDeletedEvent<TProvider>> where TProviderDefinition : ProviderDefinition, new() where TBulkProviderResource : ProviderBulkResource<TBulkProviderResource>, new() where TProvider : IProvider @@ -20,18 +28,20 @@ namespace Prowlarr.Api.V1 protected readonly ProviderResourceMapper<TProviderResource, TProviderDefinition> _resourceMapper; private readonly ProviderBulkResourceMapper<TBulkProviderResource, TProviderDefinition> _bulkResourceMapper; - protected ProviderControllerBase(IProviderFactory<TProvider, + protected ProviderControllerBase(IBroadcastSignalRMessage signalRBroadcaster, + IProviderFactory<TProvider, TProviderDefinition> providerFactory, string resource, ProviderResourceMapper<TProviderResource, TProviderDefinition> resourceMapper, ProviderBulkResourceMapper<TBulkProviderResource, TProviderDefinition> bulkResourceMapper) + : base(signalRBroadcaster) { _providerFactory = providerFactory; _resourceMapper = resourceMapper; _bulkResourceMapper = bulkResourceMapper; SharedValidator.RuleFor(c => c.Name).NotEmpty(); - SharedValidator.RuleFor(c => c.Name).Must((v, c) => !_providerFactory.All().Any(p => p.Name == c && p.Id != v.Id)).WithMessage("Should be unique"); + SharedValidator.RuleFor(c => c.Name).Must((v, c) => !_providerFactory.All().Any(p => p.Name.EqualsIgnoreCase(c) && p.Id != v.Id)).WithMessage("Should be unique"); SharedValidator.RuleFor(c => c.Implementation).NotEmpty(); SharedValidator.RuleFor(c => c.ConfigContract).NotEmpty(); @@ -243,6 +253,24 @@ namespace Prowlarr.Api.V1 return Json(data); } + [NonAction] + public void Handle(ProviderAddedEvent<TProvider> message) + { + BroadcastResourceChange(ModelAction.Created, message.Definition.Id); + } + + [NonAction] + public void Handle(ProviderUpdatedEvent<TProvider> message) + { + BroadcastResourceChange(ModelAction.Updated, message.Definition.Id); + } + + [NonAction] + public void Handle(ProviderDeletedEvent<TProvider> message) + { + BroadcastResourceChange(ModelAction.Deleted, message.ProviderId); + } + private void Validate(TProviderDefinition definition, bool includeWarnings) { var validationResult = definition.Settings.Validate(); diff --git a/src/Prowlarr.Api.V1/Prowlarr.Api.V1.csproj b/src/Prowlarr.Api.V1/Prowlarr.Api.V1.csproj index 49878d538..15684b0fe 100644 --- a/src/Prowlarr.Api.V1/Prowlarr.Api.V1.csproj +++ b/src/Prowlarr.Api.V1/Prowlarr.Api.V1.csproj @@ -4,7 +4,8 @@ </PropertyGroup> <ItemGroup> <PackageReference Include="FluentValidation" Version="9.5.4" /> - <PackageReference Include="NLog" Version="5.2.0" /> + <PackageReference Include="NLog" Version="5.4.0" /> + <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="7.3.2" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\NzbDrone.Core\Prowlarr.Core.csproj" /> diff --git a/src/Prowlarr.Api.V1/System/Backup/BackupController.cs b/src/Prowlarr.Api.V1/System/Backup/BackupController.cs index 927026805..b2c7261b7 100644 --- a/src/Prowlarr.Api.V1/System/Backup/BackupController.cs +++ b/src/Prowlarr.Api.V1/System/Backup/BackupController.cs @@ -51,7 +51,7 @@ namespace Prowlarr.Api.V1.System.Backup } [RestDeleteById] - public void DeleteBackup(int id) + public object DeleteBackup(int id) { var backup = GetBackup(id); @@ -68,6 +68,8 @@ namespace Prowlarr.Api.V1.System.Backup } _diskProvider.DeleteFile(path); + + return new { }; } [HttpPost("restore/{id:int}")] diff --git a/src/Prowlarr.Api.V1/Tags/TagController.cs b/src/Prowlarr.Api.V1/Tags/TagController.cs index ed3aeebb9..83e9825c9 100644 --- a/src/Prowlarr.Api.V1/Tags/TagController.cs +++ b/src/Prowlarr.Api.V1/Tags/TagController.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using FluentValidation; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.Messaging.Events; @@ -20,6 +21,8 @@ namespace Prowlarr.Api.V1.Tags : base(signalRBroadcaster) { _tagService = tagService; + + SharedValidator.RuleFor(c => c.Label).NotEmpty(); } public override TagResource GetResourceById(int id) diff --git a/src/Prowlarr.Api.V1/openapi.json b/src/Prowlarr.Api.V1/openapi.json index 421c00356..35cb7d6a0 100644 --- a/src/Prowlarr.Api.V1/openapi.json +++ b/src/Prowlarr.Api.V1/openapi.json @@ -34,7 +34,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -64,7 +64,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -108,7 +108,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -136,7 +136,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -148,7 +148,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -187,7 +187,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -215,7 +215,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -241,7 +241,7 @@ }, "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -253,7 +253,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -294,7 +294,7 @@ }, "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -306,7 +306,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -337,7 +337,7 @@ }, "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -358,7 +358,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -375,7 +375,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -408,7 +408,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } }, @@ -437,7 +437,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -465,7 +465,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -488,7 +488,7 @@ } }, "500": { - "description": "Server Error" + "description": "Internal Server Error" } } } @@ -500,7 +500,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -559,7 +559,7 @@ }, "responses": { "200": { - "description": "Success" + "description": "OK" } } }, @@ -569,7 +569,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -581,7 +581,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -593,7 +593,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -626,7 +626,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -649,7 +649,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -661,7 +661,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -684,7 +684,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -712,7 +712,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -733,7 +733,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -750,7 +750,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -783,7 +783,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -819,7 +819,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -847,7 +847,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -859,7 +859,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -888,7 +888,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -926,7 +926,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -954,7 +954,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -973,7 +973,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1003,7 +1003,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1047,7 +1047,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1075,7 +1075,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -1087,7 +1087,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1126,7 +1126,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1154,7 +1154,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1180,7 +1180,7 @@ }, "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -1192,7 +1192,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1233,7 +1233,7 @@ }, "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -1245,7 +1245,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -1276,7 +1276,7 @@ }, "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -1299,7 +1299,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1335,7 +1335,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1354,7 +1354,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1398,7 +1398,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -1419,7 +1419,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -1431,7 +1431,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1523,7 +1523,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1559,7 +1559,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1606,7 +1606,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1639,7 +1639,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1675,7 +1675,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1694,7 +1694,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1724,7 +1724,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1768,7 +1768,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1796,7 +1796,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -1808,7 +1808,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1847,7 +1847,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1875,7 +1875,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1901,7 +1901,7 @@ }, "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -1913,7 +1913,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -1954,7 +1954,7 @@ }, "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -1966,7 +1966,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -1997,7 +1997,7 @@ }, "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -2009,7 +2009,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -2042,7 +2042,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -2086,7 +2086,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -2114,7 +2114,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -2126,7 +2126,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -2165,7 +2165,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -2184,7 +2184,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -2225,7 +2225,7 @@ }, "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -2237,7 +2237,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -2268,7 +2268,7 @@ }, "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -2319,7 +2319,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -2338,7 +2338,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -2360,7 +2360,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -2372,7 +2372,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -2435,7 +2435,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -2454,7 +2454,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -2487,7 +2487,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -2755,7 +2755,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -3016,7 +3016,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -3053,7 +3053,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -3090,7 +3090,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -3113,7 +3113,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3157,7 +3157,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3185,7 +3185,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -3197,7 +3197,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3236,7 +3236,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3255,7 +3255,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3296,7 +3296,7 @@ }, "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -3308,7 +3308,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -3339,7 +3339,7 @@ }, "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -3351,7 +3351,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3368,7 +3368,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3396,7 +3396,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3467,7 +3467,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3501,7 +3501,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3531,7 +3531,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -3553,7 +3553,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -3576,7 +3576,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -3588,7 +3588,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3607,7 +3607,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -3619,7 +3619,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -3631,7 +3631,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -3643,7 +3643,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -3666,7 +3666,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3702,7 +3702,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3730,7 +3730,7 @@ ], "responses": { "200": { - "description": "Success" + "description": "OK" } } } @@ -3742,7 +3742,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3771,7 +3771,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3801,7 +3801,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3820,7 +3820,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3842,7 +3842,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3875,7 +3875,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3913,7 +3913,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3941,7 +3941,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3960,7 +3960,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -3979,7 +3979,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -4001,7 +4001,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "application/json": { "schema": { @@ -4034,7 +4034,7 @@ ], "responses": { "200": { - "description": "Success", + "description": "OK", "content": { "text/plain": { "schema": { @@ -4378,7 +4378,9 @@ "nullable": true }, "duration": { - "$ref": "#/components/schemas/TimeSpan" + "type": "string", + "format": "date-span", + "nullable": true }, "exception": { "type": "string", @@ -4737,7 +4739,8 @@ "nullable": true }, "wikiUrl": { - "$ref": "#/components/schemas/HttpUri" + "type": "string", + "nullable": true } }, "additionalProperties": false @@ -4871,6 +4874,10 @@ "type": "string", "nullable": true }, + "logSizeLimit": { + "type": "integer", + "format": "int32" + }, "consoleLogLevel": { "type": "string", "nullable": true @@ -4960,6 +4967,9 @@ "historyCleanupDays": { "type": "integer", "format": "int32" + }, + "trustCgnatIpAddresses": { + "type": "boolean" } }, "additionalProperties": false @@ -4982,48 +4992,6 @@ }, "additionalProperties": false }, - "HttpUri": { - "type": "object", - "properties": { - "fullUri": { - "type": "string", - "nullable": true, - "readOnly": true - }, - "scheme": { - "type": "string", - "nullable": true, - "readOnly": true - }, - "host": { - "type": "string", - "nullable": true, - "readOnly": true - }, - "port": { - "type": "integer", - "format": "int32", - "nullable": true, - "readOnly": true - }, - "path": { - "type": "string", - "nullable": true, - "readOnly": true - }, - "query": { - "type": "string", - "nullable": true, - "readOnly": true - }, - "fragment": { - "type": "string", - "nullable": true, - "readOnly": true - } - }, - "additionalProperties": false - }, "IActionResult": { "type": "object", "additionalProperties": false @@ -5083,6 +5051,10 @@ "type": "integer", "format": "int32", "nullable": true + }, + "preferMagnetUrl": { + "type": "boolean", + "nullable": true } }, "additionalProperties": false @@ -5410,6 +5382,10 @@ "type": "integer", "format": "int32" }, + "averageGrabResponseTime": { + "type": "integer", + "format": "int32" + }, "numberOfQueries": { "type": "integer", "format": "int32" @@ -6050,7 +6026,8 @@ "$ref": "#/components/schemas/DatabaseType" }, "databaseVersion": { - "$ref": "#/components/schemas/Version" + "type": "string", + "nullable": true }, "authentication": { "$ref": "#/components/schemas/AuthenticationType" @@ -6064,7 +6041,8 @@ "nullable": true }, "runtimeVersion": { - "$ref": "#/components/schemas/Version" + "type": "string", + "nullable": true }, "runtimeName": { "type": "string", @@ -6184,66 +6162,8 @@ "format": "date-time" }, "lastDuration": { - "$ref": "#/components/schemas/TimeSpan" - } - }, - "additionalProperties": false - }, - "TimeSpan": { - "type": "object", - "properties": { - "ticks": { - "type": "integer", - "format": "int64" - }, - "days": { - "type": "integer", - "format": "int32", - "readOnly": true - }, - "hours": { - "type": "integer", - "format": "int32", - "readOnly": true - }, - "milliseconds": { - "type": "integer", - "format": "int32", - "readOnly": true - }, - "minutes": { - "type": "integer", - "format": "int32", - "readOnly": true - }, - "seconds": { - "type": "integer", - "format": "int32", - "readOnly": true - }, - "totalDays": { - "type": "number", - "format": "double", - "readOnly": true - }, - "totalHours": { - "type": "number", - "format": "double", - "readOnly": true - }, - "totalMilliseconds": { - "type": "number", - "format": "double", - "readOnly": true - }, - "totalMinutes": { - "type": "number", - "format": "double", - "readOnly": true - }, - "totalSeconds": { - "type": "number", - "format": "double", + "type": "string", + "format": "date-span", "readOnly": true } }, @@ -6348,7 +6268,8 @@ "format": "int32" }, "version": { - "$ref": "#/components/schemas/Version" + "type": "string", + "nullable": true }, "branch": { "type": "string", @@ -6407,42 +6328,6 @@ } }, "additionalProperties": false - }, - "Version": { - "type": "object", - "properties": { - "major": { - "type": "integer", - "format": "int32", - "readOnly": true - }, - "minor": { - "type": "integer", - "format": "int32", - "readOnly": true - }, - "build": { - "type": "integer", - "format": "int32", - "readOnly": true - }, - "revision": { - "type": "integer", - "format": "int32", - "readOnly": true - }, - "majorRevision": { - "type": "integer", - "format": "int32", - "readOnly": true - }, - "minorRevision": { - "type": "integer", - "format": "int32", - "readOnly": true - } - }, - "additionalProperties": false } }, "securitySchemes": { @@ -6454,7 +6339,7 @@ }, "apikey": { "type": "apiKey", - "description": "Apikey passed as header", + "description": "Apikey passed as query parameter", "name": "apikey", "in": "query" } diff --git a/src/Prowlarr.Benchmark.Test/Prowlarr.Benchmark.Test.csproj b/src/Prowlarr.Benchmark.Test/Prowlarr.Benchmark.Test.csproj index 1559caafc..9bbb577cb 100644 --- a/src/Prowlarr.Benchmark.Test/Prowlarr.Benchmark.Test.csproj +++ b/src/Prowlarr.Benchmark.Test/Prowlarr.Benchmark.Test.csproj @@ -6,7 +6,7 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="BenchmarkDotNet" Version="0.13.12" /> + <PackageReference Include="BenchmarkDotNet" Version="0.14.0" /> </ItemGroup> <ItemGroup> diff --git a/src/Prowlarr.Http/Authentication/AuthenticationBuilderExtensions.cs b/src/Prowlarr.Http/Authentication/AuthenticationBuilderExtensions.cs index 9a9c54627..318f22929 100644 --- a/src/Prowlarr.Http/Authentication/AuthenticationBuilderExtensions.cs +++ b/src/Prowlarr.Http/Authentication/AuthenticationBuilderExtensions.cs @@ -1,12 +1,18 @@ using System; +using System.Text.RegularExpressions; +using Diacritical; using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.Extensions.DependencyInjection; using NzbDrone.Core.Authentication; +using NzbDrone.Core.Configuration; namespace Prowlarr.Http.Authentication { public static class AuthenticationBuilderExtensions { + private static readonly Regex CookieNameRegex = new Regex(@"[^a-z0-9]+", RegexOptions.Compiled | RegexOptions.IgnoreCase); + public static AuthenticationBuilder AddApiKey(this AuthenticationBuilder authenticationBuilder, string name, Action<ApiKeyAuthenticationOptions> options) { return authenticationBuilder.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(name, options); @@ -29,19 +35,27 @@ namespace Prowlarr.Http.Authentication public static AuthenticationBuilder AddAppAuthentication(this IServiceCollection services) { - return services.AddAuthentication() - .AddNone(AuthenticationType.None.ToString()) - .AddExternal(AuthenticationType.External.ToString()) - .AddBasic(AuthenticationType.Basic.ToString()) - .AddCookie(AuthenticationType.Forms.ToString(), options => + services.AddOptions<CookieAuthenticationOptions>(AuthenticationType.Forms.ToString()) + .Configure<IConfigFileProvider>((options, configFileProvider) => { - options.Cookie.Name = "ProwlarrAuth"; + // Replace diacritics and replace non-word characters to ensure cookie name doesn't contain any valid URL characters not allowed in cookie names + var instanceName = configFileProvider.InstanceName; + instanceName = instanceName.RemoveDiacritics(); + instanceName = CookieNameRegex.Replace(instanceName, string.Empty); + + options.Cookie.Name = $"{instanceName}Auth"; options.AccessDeniedPath = "/login?loginFailed=true"; options.LoginPath = "/login"; options.ExpireTimeSpan = TimeSpan.FromDays(7); options.SlidingExpiration = true; options.ReturnUrlParameter = "returnUrl"; - }) + }); + + return services.AddAuthentication() + .AddNone(AuthenticationType.None.ToString()) + .AddExternal(AuthenticationType.External.ToString()) + .AddBasic(AuthenticationType.Basic.ToString()) + .AddCookie(AuthenticationType.Forms.ToString()) .AddApiKey("API", options => { options.HeaderName = "X-Api-Key"; diff --git a/src/Prowlarr.Http/Authentication/AuthenticationController.cs b/src/Prowlarr.Http/Authentication/AuthenticationController.cs index 8bfec4318..05b058895 100644 --- a/src/Prowlarr.Http/Authentication/AuthenticationController.cs +++ b/src/Prowlarr.Http/Authentication/AuthenticationController.cs @@ -1,9 +1,14 @@ using System.Collections.Generic; +using System.IO; using System.Security.Claims; +using System.Security.Cryptography; using System.Threading.Tasks; +using System.Xml; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using NLog; +using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; using NzbDrone.Core.Authentication; using NzbDrone.Core.Configuration; @@ -16,11 +21,15 @@ namespace Prowlarr.Http.Authentication { private readonly IAuthenticationService _authService; private readonly IConfigFileProvider _configFileProvider; + private readonly IAppFolderInfo _appFolderInfo; + private readonly Logger _logger; - public AuthenticationController(IAuthenticationService authService, IConfigFileProvider configFileProvider) + public AuthenticationController(IAuthenticationService authService, IConfigFileProvider configFileProvider, IAppFolderInfo appFolderInfo, Logger logger) { _authService = authService; _configFileProvider = configFileProvider; + _appFolderInfo = appFolderInfo; + _logger = logger; } [HttpPost("login")] @@ -45,7 +54,23 @@ namespace Prowlarr.Http.Authentication IsPersistent = resource.RememberMe == "on" }; - await HttpContext.SignInAsync(AuthenticationType.Forms.ToString(), new ClaimsPrincipal(new ClaimsIdentity(claims, "Cookies", "user", "identifier")), authProperties); + try + { + await HttpContext.SignInAsync(AuthenticationType.Forms.ToString(), new ClaimsPrincipal(new ClaimsIdentity(claims, "Cookies", "user", "identifier")), authProperties); + } + catch (CryptographicException e) + { + if (e.InnerException is XmlException) + { + _logger.Error(e, "Failed to authenticate user due to corrupt XML. Please remove all XML files from {0} and restart Prowlarr", Path.Combine(_appFolderInfo.AppDataFolder, "asp")); + } + else + { + _logger.Error(e, "Failed to authenticate user. {0}", e.Message); + } + + return Unauthorized(); + } if (returnUrl.IsNullOrWhiteSpace()) { diff --git a/src/Prowlarr.Http/Authentication/UiAuthorizationHandler.cs b/src/Prowlarr.Http/Authentication/UiAuthorizationHandler.cs index 738364748..7ec3b5220 100644 --- a/src/Prowlarr.Http/Authentication/UiAuthorizationHandler.cs +++ b/src/Prowlarr.Http/Authentication/UiAuthorizationHandler.cs @@ -27,10 +27,13 @@ namespace Prowlarr.Http.Authentication if (_authenticationRequired == AuthenticationRequiredType.DisabledForLocalAddresses) { if (context.Resource is HttpContext httpContext && - IPAddress.TryParse(httpContext.GetRemoteIP(), out var ipAddress) && - ipAddress.IsLocalAddress()) + IPAddress.TryParse(httpContext.GetRemoteIP(), out var ipAddress)) { - context.Succeed(requirement); + if (ipAddress.IsLocalAddress() || + (_configService.TrustCgnatIpAddresses && ipAddress.IsCgnatIpAddress())) + { + context.Succeed(requirement); + } } } diff --git a/src/Prowlarr.Http/Middleware/UrlBaseMiddleware.cs b/src/Prowlarr.Http/Middleware/UrlBaseMiddleware.cs index 4cd37c24e..cf7e7b5cf 100644 --- a/src/Prowlarr.Http/Middleware/UrlBaseMiddleware.cs +++ b/src/Prowlarr.Http/Middleware/UrlBaseMiddleware.cs @@ -20,6 +20,8 @@ namespace Prowlarr.Http.Middleware if (_urlBase.IsNotNullOrWhiteSpace() && context.Request.PathBase.Value.IsNullOrWhiteSpace()) { context.Response.Redirect($"{_urlBase}{context.Request.Path}{context.Request.QueryString}"); + context.Response.StatusCode = 307; + return; } diff --git a/src/Prowlarr.Http/PagingResource.cs b/src/Prowlarr.Http/PagingResource.cs index d21b5bf42..a442e812e 100644 --- a/src/Prowlarr.Http/PagingResource.cs +++ b/src/Prowlarr.Http/PagingResource.cs @@ -38,7 +38,11 @@ namespace Prowlarr.Http public static class PagingResourceMapper { - public static PagingSpec<TModel> MapToPagingSpec<TResource, TModel>(this PagingResource<TResource> pagingResource, string defaultSortKey = "Id", SortDirection defaultSortDirection = SortDirection.Ascending) + public static PagingSpec<TModel> MapToPagingSpec<TResource, TModel>( + this PagingResource<TResource> pagingResource, + HashSet<string> allowedSortKeys, + string defaultSortKey = "id", + SortDirection defaultSortDirection = SortDirection.Ascending) { var pagingSpec = new PagingSpec<TModel> { @@ -48,14 +52,15 @@ namespace Prowlarr.Http SortDirection = pagingResource.SortDirection, }; - if (pagingResource.SortKey == null) - { - pagingSpec.SortKey = defaultSortKey; - if (pagingResource.SortDirection == SortDirection.Default) - { - pagingSpec.SortDirection = defaultSortDirection; - } - } + pagingSpec.SortKey = pagingResource.SortKey != null && + allowedSortKeys is { Count: > 0 } && + allowedSortKeys.Contains(pagingResource.SortKey) + ? pagingResource.SortKey + : defaultSortKey; + + pagingSpec.SortDirection = pagingResource.SortDirection == SortDirection.Default + ? defaultSortDirection + : pagingResource.SortDirection; return pagingSpec; } diff --git a/src/Prowlarr.Http/Prowlarr.Http.csproj b/src/Prowlarr.Http/Prowlarr.Http.csproj index 39bf131f8..e326e5ed3 100644 --- a/src/Prowlarr.Http/Prowlarr.Http.csproj +++ b/src/Prowlarr.Http/Prowlarr.Http.csproj @@ -4,8 +4,8 @@ </PropertyGroup> <ItemGroup> <PackageReference Include="FluentValidation" Version="9.5.4" /> - <PackageReference Include="ImpromptuInterface" Version="7.0.1" /> - <PackageReference Include="NLog" Version="5.2.0" /> + <PackageReference Include="ImpromptuInterface" Version="7.0.1" /> + <PackageReference Include="NLog" Version="5.4.0" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\NzbDrone.Core\Prowlarr.Core.csproj" /> diff --git a/yarn.lock b/yarn.lock index cec16a2db..c63e95086 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,15 +2,10 @@ # yarn lockfile v1 -"@aashutoshrathi/word-wrap@^1.2.3": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" - integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== - "@adobe/css-tools@^4.0.1": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.3.tgz#90749bde8b89cd41764224f5aac29cd4138f75ff" - integrity sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ== + version "4.4.0" + resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.0.tgz#728c484f4e10df03d5a3acd0d8adcbbebff8ad63" + integrity sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ== "@ampproject/remapping@^2.2.0": version "2.3.0" @@ -20,112 +15,145 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.1", "@babel/code-frame@^7.24.2": - version "7.24.2" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae" - integrity sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.16.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.25.7.tgz#438f2c524071531d643c6f0188e1e28f130cebc7" + integrity sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g== dependencies: - "@babel/highlight" "^7.24.2" + "@babel/highlight" "^7.25.7" picocolors "^1.0.0" -"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.23.5", "@babel/compat-data@^7.24.4": - version "7.24.4" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.4.tgz#6f102372e9094f25d908ca0d34fc74c74606059a" - integrity sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ== +"@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" -"@babel/core@7.24.4": - version "7.24.4" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.4.tgz#1f758428e88e0d8c563874741bc4ffc4f71a4717" - integrity sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg== +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.25.7": + version "7.25.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.25.8.tgz#0376e83df5ab0eb0da18885c0140041f0747a402" + integrity sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA== + +"@babel/compat-data@^7.25.9", "@babel/compat-data@^7.26.0": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.3.tgz#99488264a56b2aded63983abd6a417f03b92ed02" + integrity sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g== + +"@babel/core@7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40" + integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.24.2" - "@babel/generator" "^7.24.4" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.24.4" - "@babel/parser" "^7.24.4" - "@babel/template" "^7.24.0" - "@babel/traverse" "^7.24.1" - "@babel/types" "^7.24.0" + "@babel/code-frame" "^7.26.0" + "@babel/generator" "^7.26.0" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.0" + "@babel/parser" "^7.26.0" + "@babel/template" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.26.0" convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.3" semver "^6.3.1" -"@babel/eslint-parser@7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.24.1.tgz#e27eee93ed1d271637165ef3a86e2b9332395c32" - integrity sha512-d5guuzMlPeDfZIbpQ8+g1NaCNuAGBBGNECh0HVqz1sjOeVLh2CEaifuOysCH18URW6R7pqXINvf5PaR/dC6jLQ== +"@babel/eslint-parser@7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.25.9.tgz#603c68a63078796527bc9d0833f5e52dd5f9224c" + integrity sha512-5UXfgpK0j0Xr/xIdgdLEhOFxaDZ0bRPWJJchRpqOSur/3rZoPbqqki5mm0p4NE2cs28krBEiSM2MB7//afRSQQ== dependencies: "@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1" eslint-visitor-keys "^2.1.0" semver "^6.3.1" -"@babel/generator@^7.24.1", "@babel/generator@^7.24.4": - version "7.24.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.4.tgz#1fc55532b88adf952025d5d2d1e71f946cb1c498" - integrity sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw== +"@babel/generator@^7.26.0", "@babel/generator@^7.26.3": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.3.tgz#ab8d4360544a425c90c248df7059881f4b2ce019" + integrity sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ== dependencies: - "@babel/types" "^7.24.0" + "@babel/parser" "^7.26.3" + "@babel/types" "^7.26.3" "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.25" - jsesc "^2.5.1" + jsesc "^3.0.2" -"@babel/helper-annotate-as-pure@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" - integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== +"@babel/helper-annotate-as-pure@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.7.tgz#63f02dbfa1f7cb75a9bdb832f300582f30bb8972" + integrity sha512-4xwU8StnqnlIhhioZf1tqnVWeQ9pvH/ujS8hRfw/WOza+/a+1qv69BWNy+oY231maTCWgKWhfBU7kDpsds6zAA== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.25.7" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz#5426b109cf3ad47b91120f8328d8ab1be8b0b956" - integrity sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw== +"@babel/helper-annotate-as-pure@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" + integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== dependencies: - "@babel/types" "^7.22.15" + "@babel/types" "^7.25.9" -"@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.23.6": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" - integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== +"@babel/helper-compilation-targets@^7.22.6": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz#11260ac3322dda0ef53edfae6e97b961449f5fa4" + integrity sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A== dependencies: - "@babel/compat-data" "^7.23.5" - "@babel/helper-validator-option" "^7.23.5" - browserslist "^4.22.2" + "@babel/compat-data" "^7.25.7" + "@babel/helper-validator-option" "^7.25.7" + browserslist "^4.24.0" lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.24.1", "@babel/helper-create-class-features-plugin@^7.24.4": - version "7.24.4" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.4.tgz#c806f73788a6800a5cfbbc04d2df7ee4d927cce3" - integrity sha512-lG75yeuUSVu0pIcbhiYMXBXANHrpUPaOfu7ryAzskCgKUHuAxRQI5ssrtmF0X9UXldPlvT0XM/A4F44OXRt6iQ== +"@babel/helper-compilation-targets@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz#55af025ce365be3cdc0c1c1e56c6af617ce88875" + integrity sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ== dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-member-expression-to-functions" "^7.23.0" - "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/helper-replace-supers" "^7.24.1" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/compat-data" "^7.25.9" + "@babel/helper-validator-option" "^7.25.9" + browserslist "^4.24.0" + lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.15", "@babel/helper-create-regexp-features-plugin@^7.22.5": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz#5ee90093914ea09639b01c711db0d6775e558be1" - integrity sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w== +"@babel/helper-create-class-features-plugin@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz#7644147706bb90ff613297d49ed5266bde729f83" + integrity sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ== dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - regexpu-core "^5.3.1" + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/helper-replace-supers" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/traverse" "^7.25.9" semver "^6.3.1" -"@babel/helper-define-polyfill-provider@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.1.tgz#fadc63f0c2ff3c8d02ed905dcea747c5b0fb74fd" - integrity sha512-o7SDgTJuvx5vLKD6SFvkydkSMBvahDKGiNJzG22IZYXhiqoe9efY7zocICBgzHV4IRg5wdgl2nEL/tulKIEIbA== +"@babel/helper-create-regexp-features-plugin@^7.18.6": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.7.tgz#dcb464f0e2cdfe0c25cc2a0a59c37ab940ce894e" + integrity sha512-byHhumTj/X47wJ6C6eLpK7wW/WBEcnUeb7D0FNc/jFQnQVw7DOso3Zz5u9x/zLrFVkHa89ZGDbkAa1D54NdrCQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.7" + regexpu-core "^6.1.1" + semver "^6.3.1" + +"@babel/helper-create-regexp-features-plugin@^7.25.9": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz#5169756ecbe1d95f7866b90bb555b022595302a0" + integrity sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + regexpu-core "^6.2.0" + semver "^6.3.1" + +"@babel/helper-define-polyfill-provider@^0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz#18594f789c3594acb24cfdb4a7f7b7d2e8bd912d" + integrity sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ== dependencies: "@babel/helper-compilation-targets" "^7.22.6" "@babel/helper-plugin-utils" "^7.22.5" @@ -133,334 +161,223 @@ lodash.debounce "^4.0.8" resolve "^1.14.2" -"@babel/helper-environment-visitor@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" - integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== - -"@babel/helper-function-name@^7.22.5", "@babel/helper-function-name@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" - integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== +"@babel/helper-member-expression-to-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3" + integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ== dependencies: - "@babel/template" "^7.22.15" - "@babel/types" "^7.23.0" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" -"@babel/helper-hoist-variables@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" - integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== +"@babel/helper-module-imports@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== dependencies: - "@babel/types" "^7.22.5" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" -"@babel/helper-member-expression-to-functions@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz#9263e88cc5e41d39ec18c9a3e0eced59a3e7d366" - integrity sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA== +"@babel/helper-module-transforms@^7.25.9", "@babel/helper-module-transforms@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== dependencies: - "@babel/types" "^7.23.0" + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" -"@babel/helper-module-imports@^7.22.15", "@babel/helper-module-imports@^7.24.1": - version "7.24.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz#6ac476e6d168c7c23ff3ba3cf4f7841d46ac8128" - integrity sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg== +"@babel/helper-optimise-call-expression@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e" + integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ== dependencies: - "@babel/types" "^7.24.0" + "@babel/types" "^7.25.9" -"@babel/helper-module-transforms@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" - integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz#8ec5b21812d992e1ef88a9b068260537b6f0e36c" + integrity sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw== + +"@babel/helper-plugin-utils@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz#9cbdd63a9443a2c92a725cca7ebca12cc8dd9f46" + integrity sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw== + +"@babel/helper-remap-async-to-generator@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz#e53956ab3d5b9fb88be04b3e2f31b523afd34b92" + integrity sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw== dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-module-imports" "^7.22.15" - "@babel/helper-simple-access" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/helper-validator-identifier" "^7.22.20" + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-wrap-function" "^7.25.9" + "@babel/traverse" "^7.25.9" -"@babel/helper-optimise-call-expression@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" - integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== +"@babel/helper-replace-supers@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz#ba447224798c3da3f8713fc272b145e33da6a5c5" + integrity sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ== dependencies: - "@babel/types" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/traverse" "^7.25.9" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.24.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.24.0" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz#945681931a52f15ce879fd5b86ce2dae6d3d7f2a" - integrity sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w== - -"@babel/helper-remap-async-to-generator@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0" - integrity sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw== +"@babel/helper-skip-transparent-expression-wrappers@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" + integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA== dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-wrap-function" "^7.22.20" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" -"@babel/helper-replace-supers@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz#7085bd19d4a0b7ed8f405c1ed73ccb70f323abc1" - integrity sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ== +"@babel/helper-string-parser@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz#d50e8d37b1176207b4fe9acedec386c565a44a54" + integrity sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g== + +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + +"@babel/helper-validator-identifier@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz#77b7f60c40b15c97df735b38a66ba1d7c3e93da5" + integrity sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg== + +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + +"@babel/helper-validator-option@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz#97d1d684448228b30b506d90cace495d6f492729" + integrity sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ== + +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== + +"@babel/helper-wrap-function@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz#d99dfd595312e6c894bd7d237470025c85eea9d0" + integrity sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g== dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-member-expression-to-functions" "^7.23.0" - "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/template" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" -"@babel/helper-simple-access@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" - integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== +"@babel/helpers@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4" + integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw== dependencies: - "@babel/types" "^7.22.5" + "@babel/template" "^7.25.9" + "@babel/types" "^7.26.0" -"@babel/helper-skip-transparent-expression-wrappers@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" - integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== +"@babel/highlight@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.25.7.tgz#20383b5f442aa606e7b5e3043b0b1aafe9f37de5" + integrity sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw== dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-split-export-declaration@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" - integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-string-parser@^7.23.4": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz#f99c36d3593db9540705d0739a1f10b5e20c696e" - integrity sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ== - -"@babel/helper-validator-identifier@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" - integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== - -"@babel/helper-validator-option@^7.23.5": - version "7.23.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" - integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== - -"@babel/helper-wrap-function@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz#15352b0b9bfb10fc9c76f79f6342c00e3411a569" - integrity sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw== - dependencies: - "@babel/helper-function-name" "^7.22.5" - "@babel/template" "^7.22.15" - "@babel/types" "^7.22.19" - -"@babel/helpers@^7.24.4": - version "7.24.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.4.tgz#dc00907fd0d95da74563c142ef4cd21f2cb856b6" - integrity sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw== - dependencies: - "@babel/template" "^7.24.0" - "@babel/traverse" "^7.24.1" - "@babel/types" "^7.24.0" - -"@babel/highlight@^7.24.2": - version "7.24.2" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.2.tgz#3f539503efc83d3c59080a10e6634306e0370d26" - integrity sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA== - dependencies: - "@babel/helper-validator-identifier" "^7.22.20" + "@babel/helper-validator-identifier" "^7.25.7" chalk "^2.4.2" js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/parser@^7.24.0", "@babel/parser@^7.24.1", "@babel/parser@^7.24.4": - version "7.24.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.4.tgz#234487a110d89ad5a3ed4a8a566c36b9453e8c88" - integrity sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg== - -"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.24.4": - version "7.24.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.4.tgz#6125f0158543fb4edf1c22f322f3db67f21cb3e1" - integrity sha512-qpl6vOOEEzTLLcsuqYYo8yDtrTocmu2xkGvgNebvPjT9DTtfFYGmgDqY+rBYXNlqL4s9qLDn6xkrJv4RxAPiTA== +"@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.3": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.3.tgz#8c51c5db6ddf08134af1ddbacf16aaab48bac234" + integrity sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA== dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/types" "^7.26.3" -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.1.tgz#b645d9ba8c2bc5b7af50f0fe949f9edbeb07c8cf" - integrity sha512-y4HqEnkelJIOQGd+3g1bTeKsA5c6qM7eOn7VggGVbBc0y8MLSKHacwcIE2PplNlQSj0PqS9rrXL/nkPVK+kUNg== +"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz#cc2e53ebf0a0340777fff5ed521943e253b4d8fe" + integrity sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/traverse" "^7.25.9" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.1.tgz#da8261f2697f0f41b0855b91d3a20a1fbfd271d3" - integrity sha512-Hj791Ii4ci8HqnaKHAlLNs+zaLXb0EzSDhiAWp5VNlyvCNymYfacs64pxTxbH1znW/NcArSmwpmG9IKE/TUVVQ== +"@babel/plugin-bugfix-safari-class-field-initializer-scope@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz#af9e4fb63ccb8abcb92375b2fcfe36b60c774d30" + integrity sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/plugin-transform-optional-chaining" "^7.24.1" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.1.tgz#1181d9685984c91d657b8ddf14f0487a6bab2988" - integrity sha512-m9m/fXsXLiHfwdgydIFnpk+7jlVbnvlK5B2EKiPdLUb6WX654ZaaEWJUjk8TftRbZpK0XibovlLWX4KIZhV6jw== +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz#e8dc26fcd616e6c5bf2bd0d5a2c151d4f92a9137" + integrity sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug== dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-proposal-export-default-from@7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.24.1.tgz#d242019488277c9a5a8035e5b70de54402644b89" - integrity sha512-+0hrgGGV3xyYIjOrD/bUZk/iUwOIGuoANfRfVg1cPhYBxF+TIXSEcc42DqzBICmWsnAQ+SfKedY0bj8QD+LuMg== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz#807a667f9158acac6f6164b4beb85ad9ebc9e1d1" + integrity sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/plugin-syntax-export-default-from" "^7.24.1" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/plugin-transform-optional-chaining" "^7.25.9" + +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz#de7093f1e7deaf68eadd7cc6b07f2ab82543269e" + integrity sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/plugin-proposal-export-default-from@7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.25.9.tgz#52702be6ef8367fc8f18b8438278332beeb8f87c" + integrity sha512-ykqgwNfSnNOB+C8fV5X4mG3AVmvu+WVxcaU9xHHtBb7PCrPeweMmPjGsn8eMaeJg6SJuoUuZENeeSWaarWqonQ== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": version "7.21.0-placeholder-for-preset-env.2" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== -"@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-class-properties@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-syntax-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" - integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-dynamic-import@7.8.3", "@babel/plugin-syntax-dynamic-import@^7.8.3": +"@babel/plugin-syntax-dynamic-import@7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-export-default-from@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.24.1.tgz#a92852e694910ae4295e6e51e87b83507ed5e6e8" - integrity sha512-cNXSxv9eTkGUtd0PsNMK8Yx5xeScxfpWOUAxE+ZPAXXEcAMOC3fk7LRdXq5fvpra2pLx2p1YtkAhpUbB2SwaRA== +"@babel/plugin-syntax-import-assertions@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz#620412405058efa56e4a564903b79355020f445f" + integrity sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-export-namespace-from@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" - integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== +"@babel/plugin-syntax-import-attributes@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz#3b1412847699eea739b4f2602c74ce36f6b0b0f7" + integrity sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-import-assertions@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.1.tgz#db3aad724153a00eaac115a3fb898de544e34971" - integrity sha512-IuwnI5XnuF189t91XbxmXeCDz3qs6iDRO7GJ++wcfgeXNs/8FmIlKcpDSXNVyuLQxlwvskmI3Ct73wUODkJBlQ== +"@babel/plugin-syntax-jsx@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz#a34313a178ea56f1951599b929c1ceacee719290" + integrity sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-import-attributes@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.1.tgz#c66b966c63b714c4eec508fcf5763b1f2d381093" - integrity sha512-zhQTMH0X2nVLnb04tz+s7AMuasX8U0FnpE+nHTOhSOINjWMnopoZTxtIKsd45n4GQ/HIZLyfIpoul8e2m0DnRA== +"@babel/plugin-syntax-typescript@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399" + integrity sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - -"@babel/plugin-syntax-import-meta@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" - integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-jsx@^7.23.3", "@babel/plugin-syntax-jsx@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz#3f6ca04b8c841811dbc3c5c5f837934e0d626c10" - integrity sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA== - dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" - integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-top-level-await@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-typescript@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz#b3bcc51f396d15f3591683f90239de143c076844" - integrity sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw== - dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-syntax-unicode-sets-regex@^7.18.6": version "7.18.6" @@ -470,521 +387,510 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-arrow-functions@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.1.tgz#2bf263617060c9cc45bcdbf492b8cc805082bf27" - integrity sha512-ngT/3NkRhsaep9ck9uj2Xhv9+xB1zShY3tM3g6om4xxCELwCDN4g4Aq5dRn48+0hasAql7s2hdBOysCfNpr4fw== +"@babel/plugin-transform-arrow-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz#7821d4410bee5daaadbb4cdd9a6649704e176845" + integrity sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-async-generator-functions@^7.24.3": - version "7.24.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.3.tgz#8fa7ae481b100768cc9842c8617808c5352b8b89" - integrity sha512-Qe26CMYVjpQxJ8zxM1340JFNjZaF+ISWpr1Kt/jGo+ZTUzKkfw/pphEWbRCb+lmSM6k/TOgfYLvmbHkUQ0asIg== +"@babel/plugin-transform-async-generator-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz#1b18530b077d18a407c494eb3d1d72da505283a2" + integrity sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw== dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/helper-remap-async-to-generator" "^7.22.20" - "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-remap-async-to-generator" "^7.25.9" + "@babel/traverse" "^7.25.9" -"@babel/plugin-transform-async-to-generator@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.1.tgz#0e220703b89f2216800ce7b1c53cb0cf521c37f4" - integrity sha512-AawPptitRXp1y0n4ilKcGbRYWfbbzFWz2NqNu7dacYDtFtz0CMjG64b3LQsb3KIgnf4/obcUL78hfaOS7iCUfw== +"@babel/plugin-transform-async-to-generator@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz#c80008dacae51482793e5a9c08b39a5be7e12d71" + integrity sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ== dependencies: - "@babel/helper-module-imports" "^7.24.1" - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/helper-remap-async-to-generator" "^7.22.20" + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-remap-async-to-generator" "^7.25.9" -"@babel/plugin-transform-block-scoped-functions@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.1.tgz#1c94799e20fcd5c4d4589523bbc57b7692979380" - integrity sha512-TWWC18OShZutrv9C6mye1xwtam+uNi2bnTOCBUd5sZxyHOiWbU6ztSROofIMrK84uweEZC219POICK/sTYwfgg== +"@babel/plugin-transform-block-scoped-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz#5700691dbd7abb93de300ca7be94203764fce458" + integrity sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-block-scoping@^7.24.4": - version "7.24.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.4.tgz#28f5c010b66fbb8ccdeef853bef1935c434d7012" - integrity sha512-nIFUZIpGKDf9O9ttyRXpHFpKC+X3Y5mtshZONuEUYBomAKoM4y029Jr+uB1bHGPhNmK8YXHevDtKDOLmtRrp6g== +"@babel/plugin-transform-block-scoping@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz#c33665e46b06759c93687ca0f84395b80c0473a1" + integrity sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-class-properties@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.1.tgz#bcbf1aef6ba6085cfddec9fc8d58871cf011fc29" - integrity sha512-OMLCXi0NqvJfORTaPQBwqLXHhb93wkBKZ4aNwMl6WtehO7ar+cmp+89iPEQPqxAnxsOKTaMcs3POz3rKayJ72g== +"@babel/plugin-transform-class-properties@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz#a8ce84fedb9ad512549984101fa84080a9f5f51f" + integrity sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q== dependencies: - "@babel/helper-create-class-features-plugin" "^7.24.1" - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-class-static-block@^7.24.4": - version "7.24.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.4.tgz#1a4653c0cf8ac46441ec406dece6e9bc590356a4" - integrity sha512-B8q7Pz870Hz/q9UgP8InNpY01CSLDSCyqX7zcRuv3FcPl87A2G17lASroHWaCtbdIcbYzOZ7kWmXFKbijMSmFg== +"@babel/plugin-transform-class-static-block@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz#6c8da219f4eb15cae9834ec4348ff8e9e09664a0" + integrity sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.24.4" - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-classes@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.1.tgz#5bc8fc160ed96378184bc10042af47f50884dcb1" - integrity sha512-ZTIe3W7UejJd3/3R4p7ScyyOoafetUShSf4kCqV0O7F/RiHxVj/wRaRnQlrGwflvcehNA8M42HkAiEDYZu2F1Q== +"@babel/plugin-transform-classes@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz#7152457f7880b593a63ade8a861e6e26a4469f52" + integrity sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg== dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/helper-replace-supers" "^7.24.1" - "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-replace-supers" "^7.25.9" + "@babel/traverse" "^7.25.9" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.1.tgz#bc7e787f8e021eccfb677af5f13c29a9934ed8a7" - integrity sha512-5pJGVIUfJpOS+pAqBQd+QMaTD2vCL/HcePooON6pDpHgRp4gNRmzyHTPIkXntwKsq3ayUFVfJaIKPw2pOkOcTw== +"@babel/plugin-transform-computed-properties@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz#db36492c78460e534b8852b1d5befe3c923ef10b" + integrity sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/template" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/template" "^7.25.9" -"@babel/plugin-transform-destructuring@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.1.tgz#b1e8243af4a0206841973786292b8c8dd8447345" - integrity sha512-ow8jciWqNxR3RYbSNVuF4U2Jx130nwnBnhRw6N6h1bOejNkABmcI5X5oz29K4alWX7vf1C+o6gtKXikzRKkVdw== +"@babel/plugin-transform-destructuring@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz#966ea2595c498224340883602d3cfd7a0c79cea1" + integrity sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-dotall-regex@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.1.tgz#d56913d2f12795cc9930801b84c6f8c47513ac13" - integrity sha512-p7uUxgSoZwZ2lPNMzUkqCts3xlp8n+o05ikjy7gbtFJSt9gdU88jAmtfmOxHM14noQXBxfgzf2yRWECiNVhTCw== +"@babel/plugin-transform-dotall-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz#bad7945dd07734ca52fe3ad4e872b40ed09bb09a" + integrity sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-duplicate-keys@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.1.tgz#5347a797fe82b8d09749d10e9f5b83665adbca88" - integrity sha512-msyzuUnvsjsaSaocV6L7ErfNsa5nDWL1XKNnDePLgmz+WdU4w/J8+AxBMrWfi9m4IxfL5sZQKUPQKDQeeAT6lA== +"@babel/plugin-transform-duplicate-keys@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz#8850ddf57dce2aebb4394bb434a7598031059e6d" + integrity sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-dynamic-import@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.1.tgz#2a5a49959201970dd09a5fca856cb651e44439dd" - integrity sha512-av2gdSTyXcJVdI+8aFZsCAtR29xJt0S5tas+Ef8NvBNmD1a+N/3ecMLeMBgfcK+xzsjdLDT6oHt+DFPyeqUbDA== +"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz#6f7259b4de127721a08f1e5165b852fcaa696d31" + integrity sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-exponentiation-operator@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.1.tgz#6650ebeb5bd5c012d5f5f90a26613a08162e8ba4" - integrity sha512-U1yX13dVBSwS23DEAqU+Z/PkwE9/m7QQy8Y9/+Tdb8UWYaGNDYwTLi19wqIAiROr8sXVum9A/rtiH5H0boUcTw== +"@babel/plugin-transform-dynamic-import@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz#23e917de63ed23c6600c5dd06d94669dce79f7b8" + integrity sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.15" - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-export-namespace-from@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.1.tgz#f033541fc036e3efb2dcb58eedafd4f6b8078acd" - integrity sha512-Ft38m/KFOyzKw2UaJFkWG9QnHPG/Q/2SkOrRk4pNBPg5IPZ+dOxcmkK5IyuBcxiNPyyYowPGUReyBvrvZs7IlQ== +"@babel/plugin-transform-exponentiation-operator@^7.25.9": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz#e29f01b6de302c7c2c794277a48f04a9ca7f03bc" + integrity sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-for-of@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.1.tgz#67448446b67ab6c091360ce3717e7d3a59e202fd" - integrity sha512-OxBdcnF04bpdQdR3i4giHZNZQn7cm8RQKcSwA17wAAqEELo1ZOwp5FFgeptWUQXFyT9kwHo10aqqauYkRZPCAg== +"@babel/plugin-transform-export-namespace-from@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz#90745fe55053394f554e40584cda81f2c8a402a2" + integrity sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-function-name@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.1.tgz#8cba6f7730626cc4dfe4ca2fa516215a0592b361" - integrity sha512-BXmDZpPlh7jwicKArQASrj8n22/w6iymRnvHYYd2zO30DbE277JO20/7yXJT3QxDPtiQiOxQBbZH4TpivNXIxA== +"@babel/plugin-transform-for-of@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz#4bdc7d42a213397905d89f02350c5267866d5755" + integrity sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A== dependencies: - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" -"@babel/plugin-transform-json-strings@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.1.tgz#08e6369b62ab3e8a7b61089151b161180c8299f7" - integrity sha512-U7RMFmRvoasscrIFy5xA4gIp8iWnWubnKkKuUGJjsuOH7GfbMkB+XZzeslx2kLdEGdOJDamEmCqOks6e8nv8DQ== +"@babel/plugin-transform-function-name@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz#939d956e68a606661005bfd550c4fc2ef95f7b97" + integrity sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/traverse" "^7.25.9" -"@babel/plugin-transform-literals@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.1.tgz#0a1982297af83e6b3c94972686067df588c5c096" - integrity sha512-zn9pwz8U7nCqOYIiBaOxoQOtYmMODXTJnkxG4AtX8fPmnCRYWBOHD0qcpwS9e2VDSp1zNJYpdnFMIKb8jmwu6g== +"@babel/plugin-transform-json-strings@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz#c86db407cb827cded902a90c707d2781aaa89660" + integrity sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-logical-assignment-operators@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.1.tgz#719d8aded1aa94b8fb34e3a785ae8518e24cfa40" - integrity sha512-OhN6J4Bpz+hIBqItTeWJujDOfNP+unqv/NJgyhlpSqgBTPm37KkMmZV6SYcOj+pnDbdcl1qRGV/ZiIjX9Iy34w== +"@babel/plugin-transform-literals@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz#1a1c6b4d4aa59bc4cad5b6b3a223a0abd685c9de" + integrity sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-member-expression-literals@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.1.tgz#896d23601c92f437af8b01371ad34beb75df4489" - integrity sha512-4ojai0KysTWXzHseJKa1XPNXKRbuUrhkOPY4rEGeR+7ChlJVKxFa3H3Bz+7tWaGKgJAXUWKOGmltN+u9B3+CVg== +"@babel/plugin-transform-logical-assignment-operators@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz#b19441a8c39a2fda0902900b306ea05ae1055db7" + integrity sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-modules-amd@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.1.tgz#b6d829ed15258536977e9c7cc6437814871ffa39" - integrity sha512-lAxNHi4HVtjnHd5Rxg3D5t99Xm6H7b04hUS7EHIXcUl2EV4yl1gWdqZrNzXnSrHveL9qMdbODlLF55mvgjAfaQ== +"@babel/plugin-transform-member-expression-literals@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz#63dff19763ea64a31f5e6c20957e6a25e41ed5de" + integrity sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA== dependencies: - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-modules-commonjs@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz#e71ba1d0d69e049a22bf90b3867e263823d3f1b9" - integrity sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw== +"@babel/plugin-transform-modules-amd@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz#49ba478f2295101544abd794486cd3088dddb6c5" + integrity sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw== dependencies: - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-module-transforms" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-modules-systemjs@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.1.tgz#2b9625a3d4e445babac9788daec39094e6b11e3e" - integrity sha512-mqQ3Zh9vFO1Tpmlt8QPnbwGHzNz3lpNEMxQb1kAemn/erstyqw1r9KeOlOfo3y6xAnFEcOv2tSyrXfmMk+/YZA== +"@babel/plugin-transform-modules-commonjs@^7.25.9": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz#8f011d44b20d02c3de44d8850d971d8497f981fb" + integrity sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ== dependencies: - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/helper-validator-identifier" "^7.22.20" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-modules-umd@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.1.tgz#69220c66653a19cf2c0872b9c762b9a48b8bebef" - integrity sha512-tuA3lpPj+5ITfcCluy6nWonSL7RvaG0AOTeAuvXqEKS34lnLzXpDb0dcP6K8jD0zWZFNDVly90AGFJPnm4fOYg== +"@babel/plugin-transform-modules-systemjs@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz#8bd1b43836269e3d33307151a114bcf3ba6793f8" + integrity sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA== dependencies: - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-module-transforms" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" -"@babel/plugin-transform-named-capturing-groups-regex@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" - integrity sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ== +"@babel/plugin-transform-modules-umd@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz#6710079cdd7c694db36529a1e8411e49fcbf14c9" + integrity sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-module-transforms" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-new-target@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.1.tgz#29c59988fa3d0157de1c871a28cd83096363cc34" - integrity sha512-/rurytBM34hYy0HKZQyA0nHbQgQNFm4Q/BOc9Hflxi2X3twRof7NaE5W46j4kQitm7SvACVRXsa6N/tSZxvPug== +"@babel/plugin-transform-named-capturing-groups-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz#454990ae6cc22fd2a0fa60b3a2c6f63a38064e6a" + integrity sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-nullish-coalescing-operator@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.1.tgz#0cd494bb97cb07d428bd651632cb9d4140513988" - integrity sha512-iQ+caew8wRrhCikO5DrUYx0mrmdhkaELgFa+7baMcVuhxIkN7oxt06CZ51D65ugIb1UWRQ8oQe+HXAVM6qHFjw== +"@babel/plugin-transform-new-target@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz#42e61711294b105c248336dcb04b77054ea8becd" + integrity sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-numeric-separator@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.1.tgz#5bc019ce5b3435c1cadf37215e55e433d674d4e8" - integrity sha512-7GAsGlK4cNL2OExJH1DzmDeKnRv/LXq0eLUSvudrehVA5Rgg4bIrqEUW29FbKMBRT0ztSqisv7kjP+XIC4ZMNw== +"@babel/plugin-transform-nullish-coalescing-operator@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz#bcb1b0d9e948168102d5f7104375ca21c3266949" + integrity sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-object-rest-spread@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.1.tgz#5a3ce73caf0e7871a02e1c31e8b473093af241ff" - integrity sha512-XjD5f0YqOtebto4HGISLNfiNMTTs6tbkFf2TOqJlYKYmbo+mN9Dnpl4SRoofiziuOWMIyq3sZEUqLo3hLITFEA== +"@babel/plugin-transform-numeric-separator@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz#bfed75866261a8b643468b0ccfd275f2033214a1" + integrity sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q== dependencies: - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.24.1" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-object-super@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.1.tgz#e71d6ab13483cca89ed95a474f542bbfc20a0520" - integrity sha512-oKJqR3TeI5hSLRxudMjFQ9re9fBVUU0GICqM3J1mi8MqlhVr6hC/ZN4ttAyMuQR6EZZIY6h/exe5swqGNNIkWQ== +"@babel/plugin-transform-object-rest-spread@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz#0203725025074164808bcf1a2cfa90c652c99f18" + integrity sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/helper-replace-supers" "^7.24.1" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-parameters" "^7.25.9" -"@babel/plugin-transform-optional-catch-binding@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.1.tgz#92a3d0efe847ba722f1a4508669b23134669e2da" - integrity sha512-oBTH7oURV4Y+3EUrf6cWn1OHio3qG/PVwO5J03iSJmBg6m2EhKjkAu/xuaXaYwWW9miYtvbWv4LNf0AmR43LUA== +"@babel/plugin-transform-object-super@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz#385d5de135162933beb4a3d227a2b7e52bb4cf03" + integrity sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-replace-supers" "^7.25.9" -"@babel/plugin-transform-optional-chaining@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.1.tgz#26e588acbedce1ab3519ac40cc748e380c5291e6" - integrity sha512-n03wmDt+987qXwAgcBlnUUivrZBPZ8z1plL0YvgQalLm+ZE5BMhGm94jhxXtA1wzv1Cu2aaOv1BM9vbVttrzSg== +"@babel/plugin-transform-optional-catch-binding@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz#10e70d96d52bb1f10c5caaac59ac545ea2ba7ff3" + integrity sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-parameters@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.1.tgz#983c15d114da190506c75b616ceb0f817afcc510" - integrity sha512-8Jl6V24g+Uw5OGPeWNKrKqXPDw2YDjLc53ojwfMcKwlEoETKU9rU0mHUtcg9JntWI/QYzGAXNWEcVHZ+fR+XXg== +"@babel/plugin-transform-optional-chaining@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz#e142eb899d26ef715435f201ab6e139541eee7dd" + integrity sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" -"@babel/plugin-transform-private-methods@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.1.tgz#a0faa1ae87eff077e1e47a5ec81c3aef383dc15a" - integrity sha512-tGvisebwBO5em4PaYNqt4fkw56K2VALsAbAakY0FjTYqJp7gfdrgr7YX76Or8/cpik0W6+tj3rZ0uHU9Oil4tw== +"@babel/plugin-transform-parameters@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz#b856842205b3e77e18b7a7a1b94958069c7ba257" + integrity sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g== dependencies: - "@babel/helper-create-class-features-plugin" "^7.24.1" - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-private-property-in-object@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.1.tgz#756443d400274f8fb7896742962cc1b9f25c1f6a" - integrity sha512-pTHxDVa0BpUbvAgX3Gat+7cSciXqUcY9j2VZKTbSB6+VQGpNgNO9ailxTGHSXlqOnX1Hcx1Enme2+yv7VqP9bg== +"@babel/plugin-transform-private-methods@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57" + integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw== dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.24.1" - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-property-literals@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.1.tgz#d6a9aeab96f03749f4eebeb0b6ea8e90ec958825" - integrity sha512-LetvD7CrHmEx0G442gOomRr66d7q8HzzGGr4PMHGr+5YIm6++Yke+jxj246rpvsbyhJwCLxcTn6zW1P1BSenqA== +"@babel/plugin-transform-private-property-in-object@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz#9c8b73e64e6cc3cbb2743633885a7dd2c385fe33" + integrity sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-react-display-name@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.1.tgz#554e3e1a25d181f040cf698b93fd289a03bfdcdb" - integrity sha512-mvoQg2f9p2qlpDQRBC7M3c3XTr0k7cp/0+kFKKO/7Gtu0LSw16eKB+Fabe2bDT/UpsyasTBBkAnbdsLrkD5XMw== +"@babel/plugin-transform-property-literals@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz#d72d588bd88b0dec8b62e36f6fda91cedfe28e3f" + integrity sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-react-jsx-development@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz#e716b6edbef972a92165cd69d92f1255f7e73e87" - integrity sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A== +"@babel/plugin-transform-react-display-name@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz#4b79746b59efa1f38c8695065a92a9f5afb24f7d" + integrity sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ== dependencies: - "@babel/plugin-transform-react-jsx" "^7.22.5" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-react-jsx@^7.22.5", "@babel/plugin-transform-react-jsx@^7.23.4": - version "7.23.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz#393f99185110cea87184ea47bcb4a7b0c2e39312" - integrity sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA== +"@babel/plugin-transform-react-jsx-development@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz#8fd220a77dd139c07e25225a903b8be8c829e0d7" + integrity sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw== dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-module-imports" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-jsx" "^7.23.3" - "@babel/types" "^7.23.4" + "@babel/plugin-transform-react-jsx" "^7.25.9" -"@babel/plugin-transform-react-pure-annotations@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.1.tgz#c86bce22a53956331210d268e49a0ff06e392470" - integrity sha512-+pWEAaDJvSm9aFvJNpLiM2+ktl2Sn2U5DdyiWdZBxmLc6+xGt88dvFqsHiAiDS+8WqUwbDfkKz9jRxK3M0k+kA== +"@babel/plugin-transform-react-jsx@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz#06367940d8325b36edff5e2b9cbe782947ca4166" + integrity sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw== dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-syntax-jsx" "^7.25.9" + "@babel/types" "^7.25.9" -"@babel/plugin-transform-regenerator@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.1.tgz#625b7545bae52363bdc1fbbdc7252b5046409c8c" - integrity sha512-sJwZBCzIBE4t+5Q4IGLaaun5ExVMRY0lYwos/jNecjMrVCygCdph3IKv0tkP5Fc87e/1+bebAmEAGBfnRD+cnw== +"@babel/plugin-transform-react-pure-annotations@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz#ea1c11b2f9dbb8e2d97025f43a3b5bc47e18ae62" + integrity sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-regenerator@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz#03a8a4670d6cebae95305ac6defac81ece77740b" + integrity sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" regenerator-transform "^0.15.2" -"@babel/plugin-transform-reserved-words@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.1.tgz#8de729f5ecbaaf5cf83b67de13bad38a21be57c1" - integrity sha512-JAclqStUfIwKN15HrsQADFgeZt+wexNQ0uLhuqvqAUFoqPMjEcFCYZBhq0LUdz6dZK/mD+rErhW71fbx8RYElg== +"@babel/plugin-transform-regexp-modifiers@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz#2f5837a5b5cd3842a919d8147e9903cc7455b850" + integrity sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-shorthand-properties@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.1.tgz#ba9a09144cf55d35ec6b93a32253becad8ee5b55" - integrity sha512-LyjVB1nsJ6gTTUKRjRWx9C1s9hE7dLfP/knKdrfeH9UPtAGjYGgxIbFfx7xyLIEWs7Xe1Gnf8EWiUqfjLhInZA== +"@babel/plugin-transform-reserved-words@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz#0398aed2f1f10ba3f78a93db219b27ef417fb9ce" + integrity sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-spread@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.1.tgz#a1acf9152cbf690e4da0ba10790b3ac7d2b2b391" - integrity sha512-KjmcIM+fxgY+KxPVbjelJC6hrH1CgtPmTvdXAfn3/a9CnWGSTY7nH4zm5+cjmWJybdcPSsD0++QssDsjcpe47g== +"@babel/plugin-transform-shorthand-properties@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz#bb785e6091f99f826a95f9894fc16fde61c163f2" + integrity sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-sticky-regex@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.1.tgz#f03e672912c6e203ed8d6e0271d9c2113dc031b9" - integrity sha512-9v0f1bRXgPVcPrngOQvLXeGNNVLc8UjMVfebo9ka0WF3/7+aVUHmaJVT3sa0XCzEFioPfPHZiOcYG9qOsH63cw== +"@babel/plugin-transform-spread@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz#24a35153931b4ba3d13cec4a7748c21ab5514ef9" + integrity sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" -"@babel/plugin-transform-template-literals@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.1.tgz#15e2166873a30d8617e3e2ccadb86643d327aab7" - integrity sha512-WRkhROsNzriarqECASCNu/nojeXCDTE/F2HmRgOzi7NGvyfYGq1NEjKBK3ckLfRgGc6/lPAqP0vDOSw3YtG34g== +"@babel/plugin-transform-sticky-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz#c7f02b944e986a417817b20ba2c504dfc1453d32" + integrity sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-typeof-symbol@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.1.tgz#6831f78647080dec044f7e9f68003d99424f94c7" - integrity sha512-CBfU4l/A+KruSUoW+vTQthwcAdwuqbpRNB8HQKlZABwHRhsdHZ9fezp4Sn18PeAlYxTNiLMlx4xUBV3AWfg1BA== +"@babel/plugin-transform-template-literals@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz#6dbd4a24e8fad024df76d1fac6a03cf413f60fe1" + integrity sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-typescript@^7.24.1": - version "7.24.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.4.tgz#03e0492537a4b953e491f53f2bc88245574ebd15" - integrity sha512-79t3CQ8+oBGk/80SQ8MN3Bs3obf83zJ0YZjDmDaEZN8MqhMI760apl5z6a20kFeMXBwJX99VpKT8CKxEBp5H1g== +"@babel/plugin-transform-typeof-symbol@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz#224ba48a92869ddbf81f9b4a5f1204bbf5a2bc4b" + integrity sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA== dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.24.4" - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/plugin-syntax-typescript" "^7.24.1" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-unicode-escapes@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.1.tgz#fb3fa16676549ac7c7449db9b342614985c2a3a4" - integrity sha512-RlkVIcWT4TLI96zM660S877E7beKlQw7Ig+wqkKBiWfj0zH5Q4h50q6er4wzZKRNSYpfo6ILJ+hrJAGSX2qcNw== +"@babel/plugin-transform-typescript@^7.25.9": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.3.tgz#3d6add9c78735623317387ee26d5ada540eee3fd" + integrity sha512-6+5hpdr6mETwSKjmJUdYw0EIkATiQhnELWlE3kJFBwSg/BGIVwVaVbX+gOXBCdc7Ln1RXZxyWGecIXhUfnl7oA== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/plugin-syntax-typescript" "^7.25.9" -"@babel/plugin-transform-unicode-property-regex@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.1.tgz#56704fd4d99da81e5e9f0c0c93cabd91dbc4889e" - integrity sha512-Ss4VvlfYV5huWApFsF8/Sq0oXnGO+jB+rijFEFugTd3cwSObUSnUi88djgR5528Csl0uKlrI331kRqe56Ov2Ng== +"@babel/plugin-transform-unicode-escapes@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz#a75ef3947ce15363fccaa38e2dd9bc70b2788b82" + integrity sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-unicode-regex@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.1.tgz#57c3c191d68f998ac46b708380c1ce4d13536385" - integrity sha512-2A/94wgZgxfTsiLaQ2E36XAOdcZmGAaEEgVmxQWwZXWkGhvoHbaqXcKnU8zny4ycpu3vNqg0L/PcCiYtHtA13g== +"@babel/plugin-transform-unicode-property-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz#a901e96f2c1d071b0d1bb5dc0d3c880ce8f53dd3" + integrity sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-unicode-sets-regex@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.1.tgz#c1ea175b02afcffc9cf57a9c4658326625165b7f" - integrity sha512-fqj4WuzzS+ukpgerpAoOnMfQXwUHFxXUZUE84oL2Kao2N8uSlvcpnAidKASgsNgzZHBsHWvcm8s9FPWUhAb8fA== +"@babel/plugin-transform-unicode-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz#5eae747fe39eacf13a8bd006a4fb0b5d1fa5e9b1" + integrity sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/preset-env@7.24.4": - version "7.24.4" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.24.4.tgz#46dbbcd608771373b88f956ffb67d471dce0d23b" - integrity sha512-7Kl6cSmYkak0FK/FXjSEnLJ1N9T/WA2RkMhu17gZ/dsxKJUuTYNIylahPTzqpLyJN4WhDif8X0XK1R8Wsguo/A== +"@babel/plugin-transform-unicode-sets-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz#65114c17b4ffc20fa5b163c63c70c0d25621fabe" + integrity sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ== dependencies: - "@babel/compat-data" "^7.24.4" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/helper-validator-option" "^7.23.5" - "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.24.4" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.24.1" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.24.1" - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.24.1" + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/preset-env@7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.26.0.tgz#30e5c6bc1bcc54865bff0c5a30f6d4ccdc7fa8b1" + integrity sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw== + dependencies: + "@babel/compat-data" "^7.26.0" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-validator-option" "^7.25.9" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.25.9" + "@babel/plugin-bugfix-safari-class-field-initializer-scope" "^7.25.9" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.25.9" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.25.9" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.25.9" "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.24.1" - "@babel/plugin-syntax-import-attributes" "^7.24.1" - "@babel/plugin-syntax-import-meta" "^7.10.4" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-syntax-import-assertions" "^7.26.0" + "@babel/plugin-syntax-import-attributes" "^7.26.0" "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" - "@babel/plugin-transform-arrow-functions" "^7.24.1" - "@babel/plugin-transform-async-generator-functions" "^7.24.3" - "@babel/plugin-transform-async-to-generator" "^7.24.1" - "@babel/plugin-transform-block-scoped-functions" "^7.24.1" - "@babel/plugin-transform-block-scoping" "^7.24.4" - "@babel/plugin-transform-class-properties" "^7.24.1" - "@babel/plugin-transform-class-static-block" "^7.24.4" - "@babel/plugin-transform-classes" "^7.24.1" - "@babel/plugin-transform-computed-properties" "^7.24.1" - "@babel/plugin-transform-destructuring" "^7.24.1" - "@babel/plugin-transform-dotall-regex" "^7.24.1" - "@babel/plugin-transform-duplicate-keys" "^7.24.1" - "@babel/plugin-transform-dynamic-import" "^7.24.1" - "@babel/plugin-transform-exponentiation-operator" "^7.24.1" - "@babel/plugin-transform-export-namespace-from" "^7.24.1" - "@babel/plugin-transform-for-of" "^7.24.1" - "@babel/plugin-transform-function-name" "^7.24.1" - "@babel/plugin-transform-json-strings" "^7.24.1" - "@babel/plugin-transform-literals" "^7.24.1" - "@babel/plugin-transform-logical-assignment-operators" "^7.24.1" - "@babel/plugin-transform-member-expression-literals" "^7.24.1" - "@babel/plugin-transform-modules-amd" "^7.24.1" - "@babel/plugin-transform-modules-commonjs" "^7.24.1" - "@babel/plugin-transform-modules-systemjs" "^7.24.1" - "@babel/plugin-transform-modules-umd" "^7.24.1" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" - "@babel/plugin-transform-new-target" "^7.24.1" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.24.1" - "@babel/plugin-transform-numeric-separator" "^7.24.1" - "@babel/plugin-transform-object-rest-spread" "^7.24.1" - "@babel/plugin-transform-object-super" "^7.24.1" - "@babel/plugin-transform-optional-catch-binding" "^7.24.1" - "@babel/plugin-transform-optional-chaining" "^7.24.1" - "@babel/plugin-transform-parameters" "^7.24.1" - "@babel/plugin-transform-private-methods" "^7.24.1" - "@babel/plugin-transform-private-property-in-object" "^7.24.1" - "@babel/plugin-transform-property-literals" "^7.24.1" - "@babel/plugin-transform-regenerator" "^7.24.1" - "@babel/plugin-transform-reserved-words" "^7.24.1" - "@babel/plugin-transform-shorthand-properties" "^7.24.1" - "@babel/plugin-transform-spread" "^7.24.1" - "@babel/plugin-transform-sticky-regex" "^7.24.1" - "@babel/plugin-transform-template-literals" "^7.24.1" - "@babel/plugin-transform-typeof-symbol" "^7.24.1" - "@babel/plugin-transform-unicode-escapes" "^7.24.1" - "@babel/plugin-transform-unicode-property-regex" "^7.24.1" - "@babel/plugin-transform-unicode-regex" "^7.24.1" - "@babel/plugin-transform-unicode-sets-regex" "^7.24.1" + "@babel/plugin-transform-arrow-functions" "^7.25.9" + "@babel/plugin-transform-async-generator-functions" "^7.25.9" + "@babel/plugin-transform-async-to-generator" "^7.25.9" + "@babel/plugin-transform-block-scoped-functions" "^7.25.9" + "@babel/plugin-transform-block-scoping" "^7.25.9" + "@babel/plugin-transform-class-properties" "^7.25.9" + "@babel/plugin-transform-class-static-block" "^7.26.0" + "@babel/plugin-transform-classes" "^7.25.9" + "@babel/plugin-transform-computed-properties" "^7.25.9" + "@babel/plugin-transform-destructuring" "^7.25.9" + "@babel/plugin-transform-dotall-regex" "^7.25.9" + "@babel/plugin-transform-duplicate-keys" "^7.25.9" + "@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.25.9" + "@babel/plugin-transform-dynamic-import" "^7.25.9" + "@babel/plugin-transform-exponentiation-operator" "^7.25.9" + "@babel/plugin-transform-export-namespace-from" "^7.25.9" + "@babel/plugin-transform-for-of" "^7.25.9" + "@babel/plugin-transform-function-name" "^7.25.9" + "@babel/plugin-transform-json-strings" "^7.25.9" + "@babel/plugin-transform-literals" "^7.25.9" + "@babel/plugin-transform-logical-assignment-operators" "^7.25.9" + "@babel/plugin-transform-member-expression-literals" "^7.25.9" + "@babel/plugin-transform-modules-amd" "^7.25.9" + "@babel/plugin-transform-modules-commonjs" "^7.25.9" + "@babel/plugin-transform-modules-systemjs" "^7.25.9" + "@babel/plugin-transform-modules-umd" "^7.25.9" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.25.9" + "@babel/plugin-transform-new-target" "^7.25.9" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.25.9" + "@babel/plugin-transform-numeric-separator" "^7.25.9" + "@babel/plugin-transform-object-rest-spread" "^7.25.9" + "@babel/plugin-transform-object-super" "^7.25.9" + "@babel/plugin-transform-optional-catch-binding" "^7.25.9" + "@babel/plugin-transform-optional-chaining" "^7.25.9" + "@babel/plugin-transform-parameters" "^7.25.9" + "@babel/plugin-transform-private-methods" "^7.25.9" + "@babel/plugin-transform-private-property-in-object" "^7.25.9" + "@babel/plugin-transform-property-literals" "^7.25.9" + "@babel/plugin-transform-regenerator" "^7.25.9" + "@babel/plugin-transform-regexp-modifiers" "^7.26.0" + "@babel/plugin-transform-reserved-words" "^7.25.9" + "@babel/plugin-transform-shorthand-properties" "^7.25.9" + "@babel/plugin-transform-spread" "^7.25.9" + "@babel/plugin-transform-sticky-regex" "^7.25.9" + "@babel/plugin-transform-template-literals" "^7.25.9" + "@babel/plugin-transform-typeof-symbol" "^7.25.9" + "@babel/plugin-transform-unicode-escapes" "^7.25.9" + "@babel/plugin-transform-unicode-property-regex" "^7.25.9" + "@babel/plugin-transform-unicode-regex" "^7.25.9" + "@babel/plugin-transform-unicode-sets-regex" "^7.25.9" "@babel/preset-modules" "0.1.6-no-external-plugins" babel-plugin-polyfill-corejs2 "^0.4.10" - babel-plugin-polyfill-corejs3 "^0.10.4" + babel-plugin-polyfill-corejs3 "^0.10.6" babel-plugin-polyfill-regenerator "^0.6.1" - core-js-compat "^3.31.0" + core-js-compat "^3.38.1" semver "^6.3.1" "@babel/preset-modules@0.1.6-no-external-plugins": @@ -996,89 +902,89 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/preset-react@7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.24.1.tgz#2450c2ac5cc498ef6101a6ca5474de251e33aa95" - integrity sha512-eFa8up2/8cZXLIpkafhaADTXSnl7IsUFCYenRWrARBz0/qZwcT0RBXpys0LJU4+WfPoF2ZG6ew6s2V6izMCwRA== +"@babel/preset-react@7.26.3": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.26.3.tgz#7c5e028d623b4683c1f83a0bd4713b9100560caa" + integrity sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/helper-validator-option" "^7.23.5" - "@babel/plugin-transform-react-display-name" "^7.24.1" - "@babel/plugin-transform-react-jsx" "^7.23.4" - "@babel/plugin-transform-react-jsx-development" "^7.22.5" - "@babel/plugin-transform-react-pure-annotations" "^7.24.1" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-validator-option" "^7.25.9" + "@babel/plugin-transform-react-display-name" "^7.25.9" + "@babel/plugin-transform-react-jsx" "^7.25.9" + "@babel/plugin-transform-react-jsx-development" "^7.25.9" + "@babel/plugin-transform-react-pure-annotations" "^7.25.9" -"@babel/preset-typescript@7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.24.1.tgz#89bdf13a3149a17b3b2a2c9c62547f06db8845ec" - integrity sha512-1DBaMmRDpuYQBPWD8Pf/WEwCrtgRHxsZnP4mIy9G/X+hFfbI47Q2G4t1Paakld84+qsk2fSsUPMKg71jkoOOaQ== +"@babel/preset-typescript@7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz#4a570f1b8d104a242d923957ffa1eaff142a106d" + integrity sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - "@babel/helper-validator-option" "^7.23.5" - "@babel/plugin-syntax-jsx" "^7.24.1" - "@babel/plugin-transform-modules-commonjs" "^7.24.1" - "@babel/plugin-transform-typescript" "^7.24.1" - -"@babel/regjsgen@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" - integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-validator-option" "^7.25.9" + "@babel/plugin-syntax-jsx" "^7.25.9" + "@babel/plugin-transform-modules-commonjs" "^7.25.9" + "@babel/plugin-transform-typescript" "^7.25.9" "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": - version "7.24.4" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.4.tgz#de795accd698007a66ba44add6cc86542aff1edd" - integrity sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA== + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.7.tgz#7ffb53c37a8f247c8c4d335e89cdf16a2e0d0fb6" + integrity sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w== dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.22.15", "@babel/template@^7.24.0": - version "7.24.0" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50" - integrity sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA== +"@babel/template@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016" + integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== dependencies: - "@babel/code-frame" "^7.23.5" - "@babel/parser" "^7.24.0" - "@babel/types" "^7.24.0" + "@babel/code-frame" "^7.25.9" + "@babel/parser" "^7.25.9" + "@babel/types" "^7.25.9" -"@babel/traverse@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.1.tgz#d65c36ac9dd17282175d1e4a3c49d5b7988f530c" - integrity sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ== +"@babel/traverse@^7.25.9": + version "7.26.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.4.tgz#ac3a2a84b908dde6d463c3bfa2c5fdc1653574bd" + integrity sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w== dependencies: - "@babel/code-frame" "^7.24.1" - "@babel/generator" "^7.24.1" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.24.1" - "@babel/types" "^7.24.0" + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.3" + "@babel/parser" "^7.26.3" + "@babel/template" "^7.25.9" + "@babel/types" "^7.26.3" debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.24.0", "@babel/types@^7.4.4": - version "7.24.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf" - integrity sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w== +"@babel/types@^7.25.7", "@babel/types@^7.4.4": + version "7.25.8" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.8.tgz#5cf6037258e8a9bcad533f4979025140cb9993e1" + integrity sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg== dependencies: - "@babel/helper-string-parser" "^7.23.4" - "@babel/helper-validator-identifier" "^7.22.20" + "@babel/helper-string-parser" "^7.25.7" + "@babel/helper-validator-identifier" "^7.25.7" to-fast-properties "^2.0.0" +"@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.3": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0" + integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@csstools/css-parser-algorithms@^2.1.1": - version "2.6.1" - resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.1.tgz#c45440d1efa2954006748a01697072dae5881bcd" - integrity sha512-ubEkAaTfVZa+WwGhs5jbo5Xfqpeaybr/RvWzvFxRs4jfq16wH8l8Ty/QEEpINxll4xhuGfdMbipRyz5QZh9+FA== + version "2.7.1" + resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.7.1.tgz#6d93a8f7d8aeb7cd9ed0868f946e46f021b6aa70" + integrity sha512-2SJS42gxmACHgikc1WGesXLIT8d/q2l0UFM7TaEeIzdFCE/FPMtTiizcPGGJtlPo2xuQzY09OhrLTzRxqJqwGw== "@csstools/css-tokenizer@^2.1.1": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-2.2.4.tgz#a4b8718ed7fcd2dcd555de16b31ca59ad4b96a06" - integrity sha512-PuWRAewQLbDhGeTvFuq2oClaSCKPIBmHyIobCV39JHRYN0byDcUWJl5baPeNUcqrjtdMNqFooE0FGl31I3JOqw== + version "2.4.1" + resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-2.4.1.tgz#1d8b2e200197cf5f35ceb07ca2dade31f3a00ae8" + integrity sha512-eQ9DIktFJBhGjioABJRtUucoWR2mwllurfnM8LuNGAqX3ViZXaUchqk+1s7jjtkFiT9ySdACsFEA3etErkALUg== "@csstools/media-query-list-parser@^2.0.4": - version "2.1.9" - resolved "https://registry.yarnpkg.com/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.9.tgz#feb4b7268f998956eb3ded69507869e73d005dda" - integrity sha512-qqGuFfbn4rUmyOB0u8CVISIp5FfJ5GAR3mBrZ9/TKndHakdnm6pY0L/fbLcpPnrzwCyyTEZl1nUcXAYHEWneTA== + version "2.1.13" + resolved "https://registry.yarnpkg.com/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.13.tgz#f00be93f6bede07c14ddf51a168ad2748e4fe9e5" + integrity sha512-XaHr+16KRU9Gf8XLi3q8kDlI18d5vzKSKCY510Vrtc9iNR0NJzbY9hhTmwhzYZj/ZwGL4VmB3TA9hJW0Um2qFA== "@csstools/selector-specificity@^2.2.0": version "2.2.0" @@ -1097,10 +1003,15 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": - version "4.10.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" - integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== +"@eslint-community/regexpp@^4.10.0": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint-community/regexpp@^4.6.1": + version "4.11.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.1.tgz#a547badfc719eb3e5f4b556325e542fbe9d7a18f" + integrity sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q== "@eslint/eslintrc@^2.1.4": version "2.1.4" @@ -1117,55 +1028,55 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.57.0": - version "8.57.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" - integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== +"@eslint/js@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" + integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== -"@fortawesome/fontawesome-common-types@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz#88da2b70d6ca18aaa6ed3687832e11f39e80624b" - integrity sha512-HNii132xfomg5QVZw0HwXXpN22s7VBHQBv9CeOu9tfJnhsWQNd2lmTNi8CSrnw5B+5YOmzu1UoPAyxaXsJ6RgQ== +"@fortawesome/fontawesome-common-types@6.7.1": + version "6.7.1" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.1.tgz#6201640f39fdcf8e41cd9d1a92b2da3a96817fa4" + integrity sha512-gbDz3TwRrIPT3i0cDfujhshnXO9z03IT1UKRIVi/VEjpNHtSBIP2o5XSm+e816FzzCFEzAxPw09Z13n20PaQJQ== -"@fortawesome/fontawesome-free@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz#1ee0c174e472c84b23cb46c995154dc383e3b4fe" - integrity sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ== +"@fortawesome/fontawesome-free@6.7.1": + version "6.7.1" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-6.7.1.tgz#160a48730d533ec77578ed0141661a8f0150a71d" + integrity sha512-ALIk/MOh5gYe1TG/ieS5mVUsk7VUIJTJKPMK9rFFqOgfp0Q3d5QiBXbcOMwUvs37fyZVCz46YjOE6IFeOAXCHA== -"@fortawesome/fontawesome-svg-core@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.0.tgz#3727552eff9179506e9203d72feb5b1063c11a21" - integrity sha512-Bertv8xOiVELz5raB2FlXDPKt+m94MQ3JgDfsVbrqNpLU9+UE2E18GKjLKw+d3XbeYPqg1pzyQKGsrzbw+pPaw== +"@fortawesome/fontawesome-svg-core@6.7.1": + version "6.7.1" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.1.tgz#1f8ebb6f35cf02f89c110198514e848de17ac99e" + integrity sha512-8dBIHbfsKlCk2jHQ9PoRBg2Z+4TwyE3vZICSnoDlnsHA6SiMlTwfmW6yX0lHsRmWJugkeb92sA0hZdkXJhuz+g== dependencies: - "@fortawesome/fontawesome-common-types" "6.4.0" + "@fortawesome/fontawesome-common-types" "6.7.1" -"@fortawesome/free-regular-svg-icons@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.4.0.tgz#cacc53bd8d832d46feead412d9ea9ce80a55e13a" - integrity sha512-ZfycI7D0KWPZtf7wtMFnQxs8qjBXArRzczABuMQqecA/nXohquJ5J/RCR77PmY5qGWkxAZDxpnUFVXKwtY/jPw== +"@fortawesome/free-regular-svg-icons@6.7.1": + version "6.7.1" + resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.7.1.tgz#d7ec06f896ee91116a388a5a234cd26420ccdfe4" + integrity sha512-e13cp+bAx716RZOTQ59DhqikAgETA9u1qTBHO3e3jMQQ+4H/N1NC1ZVeFYt1V0m+Th68BrEL1/X6XplISutbXg== dependencies: - "@fortawesome/fontawesome-common-types" "6.4.0" + "@fortawesome/fontawesome-common-types" "6.7.1" -"@fortawesome/free-solid-svg-icons@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.0.tgz#48c0e790847fa56299e2f26b82b39663b8ad7119" - integrity sha512-kutPeRGWm8V5dltFP1zGjQOEAzaLZj4StdQhWVZnfGFCvAPVvHh8qk5bRrU4KXnRRRNni5tKQI9PBAdI6MP8nQ== +"@fortawesome/free-solid-svg-icons@6.7.1": + version "6.7.1" + resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.7.1.tgz#c1f9a6c25562a12c283e87e284f9d82a5b0dbcc0" + integrity sha512-BTKc0b0mgjWZ2UDKVgmwaE0qt0cZs6ITcDgjrti5f/ki7aF5zs+N91V6hitGo3TItCFtnKg6cUVGdTmBFICFRg== dependencies: - "@fortawesome/fontawesome-common-types" "6.4.0" + "@fortawesome/fontawesome-common-types" "6.7.1" -"@fortawesome/react-fontawesome@0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz#d90dd8a9211830b4e3c08e94b63a0ba7291ddcf4" - integrity sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw== +"@fortawesome/react-fontawesome@0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz#68b058f9132b46c8599875f6a636dad231af78d4" + integrity sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g== dependencies: prop-types "^15.8.1" -"@humanwhocodes/config-array@^0.11.14": - version "0.11.14" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" - integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== +"@humanwhocodes/config-array@^0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" + integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw== dependencies: - "@humanwhocodes/object-schema" "^2.0.2" + "@humanwhocodes/object-schema" "^2.0.3" debug "^4.3.1" minimatch "^3.0.5" @@ -1174,11 +1085,23 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^2.0.2": +"@humanwhocodes/object-schema@^2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@jridgewell/gen-mapping@^0.3.5": version "0.3.5" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" @@ -1207,11 +1130,11 @@ "@jridgewell/trace-mapping" "^0.3.25" "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": - version "1.4.15" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" - integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== -"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": +"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== @@ -1283,86 +1206,92 @@ resolved "https://registry.yarnpkg.com/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz#a3031eb54129f2c66b2753f8404266ec7bf67f0a" integrity sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg== -"@sentry-internal/feedback@7.100.0": - version "7.100.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-7.100.0.tgz#38d8d4cb8ac3e6e24d91b13878bd6208a55bcab3" - integrity sha512-SMW2QhNKOuSjw8oPtvryDlJjiwrNyAKljbgtMk057os/fd8QMp38Yt1ImqLCM4B2rTQZ6REJ6hRGRTRcfqoG+w== - dependencies: - "@sentry/core" "7.100.0" - "@sentry/types" "7.100.0" - "@sentry/utils" "7.100.0" +"@rtsao/scc@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" + integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== -"@sentry-internal/replay-canvas@7.100.0": - version "7.100.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-7.100.0.tgz#b462346832631ed5a9446686419113ff331bd984" - integrity sha512-DePinj5IgNiC4RZv0yX0DLccMZebfFdKl3zHwDeLBeZqtMz9VrPzchv57IWP+5MI1+iuOn+WOg4oTNBUG6hFRw== +"@sentry-internal/feedback@7.119.1": + version "7.119.1" + resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-7.119.1.tgz#98285dc9dba0ab62369d758124901b00faf58697" + integrity sha512-EPyW6EKZmhKpw/OQUPRkTynXecZdYl4uhZwdZuGqnGMAzswPOgQvFrkwsOuPYvoMfXqCH7YuRqyJrox3uBOrTA== dependencies: - "@sentry/core" "7.100.0" - "@sentry/replay" "7.100.0" - "@sentry/types" "7.100.0" - "@sentry/utils" "7.100.0" + "@sentry/core" "7.119.1" + "@sentry/types" "7.119.1" + "@sentry/utils" "7.119.1" -"@sentry-internal/tracing@7.100.0": - version "7.100.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.100.0.tgz#01f0925a287a6e5d0becd731ab361cabbd27c007" - integrity sha512-qf4W1STXky9WOQYoPSw2AmCBDK4FzvAyq5yeD2sLU7OCUEfbRUcN0lQljUvmWRKv/jTIAyeU5icDLJPZuR50nA== +"@sentry-internal/replay-canvas@7.119.1": + version "7.119.1" + resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-7.119.1.tgz#b1413fb37734d609b0745ac24d49ddf9d63b9c51" + integrity sha512-O/lrzENbMhP/UDr7LwmfOWTjD9PLNmdaCF408Wx8SDuj7Iwc+VasGfHg7fPH4Pdr4nJON6oh+UqoV4IoG05u+A== dependencies: - "@sentry/core" "7.100.0" - "@sentry/types" "7.100.0" - "@sentry/utils" "7.100.0" + "@sentry/core" "7.119.1" + "@sentry/replay" "7.119.1" + "@sentry/types" "7.119.1" + "@sentry/utils" "7.119.1" -"@sentry/browser@7.100.0": - version "7.100.0" - resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.100.0.tgz#adf57f660baa6190a7e1709605f73b94818ee04b" - integrity sha512-XpM0jEVe6DJWXjMSOjtJxsSNR/XnJKrlcuyoI4Re3qLG+noEF5QLc0r3VJkySXPRFnmdW05sLswQ6a/n9Sijmg== +"@sentry-internal/tracing@7.119.1": + version "7.119.1" + resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.119.1.tgz#500d50d451bfd0ce6b185e9f112208229739ab03" + integrity sha512-cI0YraPd6qBwvUA3wQdPGTy8PzAoK0NZiaTN1LM3IczdPegehWOaEG5GVTnpGnTsmBAzn1xnBXNBhgiU4dgcrQ== dependencies: - "@sentry-internal/feedback" "7.100.0" - "@sentry-internal/replay-canvas" "7.100.0" - "@sentry-internal/tracing" "7.100.0" - "@sentry/core" "7.100.0" - "@sentry/replay" "7.100.0" - "@sentry/types" "7.100.0" - "@sentry/utils" "7.100.0" + "@sentry/core" "7.119.1" + "@sentry/types" "7.119.1" + "@sentry/utils" "7.119.1" -"@sentry/core@7.100.0": - version "7.100.0" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.100.0.tgz#5b28c7b3e41e45e4d50e3bdea5d35434fd78e86b" - integrity sha512-eWRPuP0Zdj4a2F7SybqNjf13LGOVgGwvW6sojweQp9oxGAfCPp/EMDGBhlpYbMJeLbzmqzJ4ZFHIedaiEC+7kg== +"@sentry/browser@7.119.1": + version "7.119.1" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.119.1.tgz#260470dd7fd18de366017c3bf23a252a24d2ff05" + integrity sha512-aMwAnFU4iAPeLyZvqmOQaEDHt/Dkf8rpgYeJ0OEi50dmP6AjG+KIAMCXU7CYCCQDn70ITJo8QD5+KzCoZPYz0A== dependencies: - "@sentry/types" "7.100.0" - "@sentry/utils" "7.100.0" + "@sentry-internal/feedback" "7.119.1" + "@sentry-internal/replay-canvas" "7.119.1" + "@sentry-internal/tracing" "7.119.1" + "@sentry/core" "7.119.1" + "@sentry/integrations" "7.119.1" + "@sentry/replay" "7.119.1" + "@sentry/types" "7.119.1" + "@sentry/utils" "7.119.1" -"@sentry/integrations@7.100.0": - version "7.100.0" - resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.100.0.tgz#6620cce950dce7c1f3e63d5b047b320a7d25b3c7" - integrity sha512-aO9wgnqlbav7FECKNcgTxQSGGSsMeYH9mV0cniuu520cDAhmVxtA+PqlnS3nsJZJj4cKjX6MWA2SbBG0szKmkw== +"@sentry/core@7.119.1": + version "7.119.1" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.119.1.tgz#63e949cad167a0ee5e52986c93b96ff1d6a05b57" + integrity sha512-YUNnH7O7paVd+UmpArWCPH4Phlb5LwrkWVqzFWqL3xPyCcTSof2RL8UmvpkTjgYJjJ+NDfq5mPFkqv3aOEn5Sw== dependencies: - "@sentry/core" "7.100.0" - "@sentry/types" "7.100.0" - "@sentry/utils" "7.100.0" + "@sentry/types" "7.119.1" + "@sentry/utils" "7.119.1" + +"@sentry/integrations@7.119.1": + version "7.119.1" + resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.119.1.tgz#9fc17aa9fcb942fbd2fc12eecd77a0f316897960" + integrity sha512-CGmLEPnaBqbUleVqrmGYjRjf5/OwjUXo57I9t0KKWViq81mWnYhaUhRZWFNoCNQHns+3+GPCOMvl0zlawt+evw== + dependencies: + "@sentry/core" "7.119.1" + "@sentry/types" "7.119.1" + "@sentry/utils" "7.119.1" localforage "^1.8.1" -"@sentry/replay@7.100.0": - version "7.100.0" - resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.100.0.tgz#4f2e35155626ab286692ade3e31da282c73bd402" - integrity sha512-6Yo56J+x+eedaMXri8pPlFxXOofnSXVdsUuFj+kJ7lC/qHrwIbgC5g1ONEK/WlYwpVH4gA0aNnCa5AOkMu+ZTg== +"@sentry/replay@7.119.1": + version "7.119.1" + resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.119.1.tgz#117cf493a3008a39943b7d571d451c6218542847" + integrity sha512-4da+ruMEipuAZf35Ybt2StBdV1S+oJbSVccGpnl9w6RoeQoloT4ztR6ML3UcFDTXeTPT1FnHWDCyOfST0O7XMw== dependencies: - "@sentry-internal/tracing" "7.100.0" - "@sentry/core" "7.100.0" - "@sentry/types" "7.100.0" - "@sentry/utils" "7.100.0" + "@sentry-internal/tracing" "7.119.1" + "@sentry/core" "7.119.1" + "@sentry/types" "7.119.1" + "@sentry/utils" "7.119.1" -"@sentry/types@7.100.0": - version "7.100.0" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.100.0.tgz#a16f60d78613bd9810298e9e8d80134be58b24f8" - integrity sha512-c+RHwZwpKeBk7h8sUX4nQcelxBz8ViCojifnbEe3tcn8O15HOLvZqRKgLLOiff3MoErxiv4oxs0sPbEFRm/IvA== +"@sentry/types@7.119.1": + version "7.119.1" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.119.1.tgz#f9c3c12e217c9078a6d556c92590e42a39b750dd" + integrity sha512-4G2mcZNnYzK3pa2PuTq+M2GcwBRY/yy1rF+HfZU+LAPZr98nzq2X3+mJHNJoobeHRkvVh7YZMPi4ogXiIS5VNQ== -"@sentry/utils@7.100.0": - version "7.100.0" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.100.0.tgz#a9d36c01eede117c3e17b0350d399a87934e9c66" - integrity sha512-LAhZMEGq3C125prZN/ShqeXpRfdfgJkl9RAKjfq8cmMFsF7nsF72dEHZgIwrZ0lgNmtaWAB83AwJcyN83RwOxQ== +"@sentry/utils@7.119.1": + version "7.119.1" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.119.1.tgz#08b28fa8170987a60e149e2102e83395a95e9a89" + integrity sha512-ju/Cvyeu/vkfC5/XBV30UNet5kLEicZmXSyuLwZu95hEbL+foPdxN+re7pCI/eNqfe3B2vz7lvz5afLVOlQ2Hg== dependencies: - "@sentry/types" "7.100.0" + "@sentry/types" "7.119.1" "@types/archiver@^5.3.1": version "5.3.4" @@ -1371,26 +1300,10 @@ dependencies: "@types/readdir-glob" "*" -"@types/eslint-scope@^3.7.3": - version "3.7.7" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" - integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== - dependencies: - "@types/eslint" "*" - "@types/estree" "*" - -"@types/eslint@*": - version "8.56.10" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.10.tgz#eb2370a73bf04a901eeba8f22595c7ee0f7eb58d" - integrity sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - -"@types/estree@*", "@types/estree@^1.0.0": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" - integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== +"@types/estree@^1.0.5": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== "@types/history@^4.7.11": version "4.7.11" @@ -1410,7 +1323,7 @@ resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== -"@types/json-schema@*", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": +"@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== @@ -1420,10 +1333,10 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/lodash@4.14.194": - version "4.14.194" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.194.tgz#b71eb6f7a0ff11bff59fc987134a093029258a76" - integrity sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g== +"@types/lodash@4.14.195": + version "4.14.195" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.195.tgz#bafc975b252eb6cea78882ce8a7b6bf22a6de632" + integrity sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg== "@types/minimist@^1.2.0": version "1.2.5" @@ -1431,18 +1344,18 @@ integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== "@types/node@*": - version "20.12.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.7.tgz#04080362fa3dd6c5822061aa3124f5c152cff384" - integrity sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg== + version "22.7.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.5.tgz#cfde981727a7ab3611a481510b473ae54442b92b" + integrity sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ== dependencies: - undici-types "~5.26.4" + undici-types "~6.19.2" -"@types/node@18.19.31": - version "18.19.31" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.31.tgz#b7d4a00f7cb826b60a543cebdbda5d189aaecdcd" - integrity sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA== +"@types/node@20.16.11": + version "20.16.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.16.11.tgz#9b544c3e716b1577ac12e70f9145193f32750b33" + integrity sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw== dependencies: - undici-types "~5.26.4" + undici-types "~6.19.2" "@types/normalize-package-data@^2.4.0": version "2.4.4" @@ -1469,9 +1382,16 @@ postcss "^8.0.0" "@types/prop-types@*": - version "15.7.12" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6" - integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q== + version "15.7.13" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.13.tgz#2af91918ee12d9d32914feb13f5326658461b451" + integrity sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA== + +"@types/react-document-title@2.0.10": + version "2.0.10" + resolved "https://registry.yarnpkg.com/@types/react-document-title/-/react-document-title-2.0.10.tgz#f9c4563744b735750d84519ba1bc7099e1b2d1d0" + integrity sha512-a5RYXFccVqVhc429yXUn9zjJvaQwdx3Kueb8v8pEymUyExHoatHv0iS8BlOE3YuS+csA2pHbL2Hatnp7QEtLxQ== + dependencies: + "@types/react" "*" "@types/react-dom@18.2.25": version "18.2.25" @@ -1481,9 +1401,9 @@ "@types/react" "*" "@types/react-redux@^7.1.16": - version "7.1.33" - resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.33.tgz#53c5564f03f1ded90904e3c90f77e4bd4dc20b15" - integrity sha512-NF8m5AjWCkert+fosDsN3hAlHzpjSiXlVy9EgQEmLoBhaNXbmyeGs/aj5dQzKuF+/q+S7JQagorGDW8pJ28Hmg== + version "7.1.34" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.34.tgz#83613e1957c481521e6776beeac4fd506d11bd0e" + integrity sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ== dependencies: "@types/hoist-non-react-statics" "^3.3.0" "@types/react" "*" @@ -1507,21 +1427,29 @@ "@types/history" "^4.7.11" "@types/react" "*" -"@types/react-text-truncate@0.14.1": - version "0.14.1" - resolved "https://registry.yarnpkg.com/@types/react-text-truncate/-/react-text-truncate-0.14.1.tgz#3d24eca927e5fd1bfd789b047ae8ec53ba878b28" - integrity sha512-yCtOOOJzrsfWF6TbnTDZz0gM5JYOxJmewExaTJTv01E7yrmpkNcmVny2fAtsNgSFCp8k2VgCePBoIvFBpKyEOw== +"@types/react-text-truncate@0.19.0": + version "0.19.0" + resolved "https://registry.yarnpkg.com/@types/react-text-truncate/-/react-text-truncate-0.19.0.tgz#322dd718dcbe1267a9d1279f8ac4dc844c322a13" + integrity sha512-8H7BjVf7Rp3ERTTiFZpQf6a5hllwdJrWuQ92nwQGp7DWQ2Ju89GRuzXHuZHXU9T+hLTGLCUPbimjQnW1mAogqQ== dependencies: "@types/react" "*" -"@types/react-window@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@types/react-window/-/react-window-1.8.5.tgz#285fcc5cea703eef78d90f499e1457e9b5c02fc1" - integrity sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw== +"@types/react-window@1.8.8": + version "1.8.8" + resolved "https://registry.yarnpkg.com/@types/react-window/-/react-window-1.8.8.tgz#c20645414d142364fbe735818e1c1e0a145696e3" + integrity sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q== dependencies: "@types/react" "*" -"@types/react@*", "@types/react@18.2.79": +"@types/react@*": + version "18.3.11" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.11.tgz#9d530601ff843ee0d7030d4227ea4360236bd537" + integrity sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + +"@types/react@18.2.79": version "18.2.79" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.79.tgz#c40efb4f255711f554d47b449f796d1c7756d865" integrity sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w== @@ -1536,11 +1464,6 @@ dependencies: "@types/node" "*" -"@types/semver@^7.5.0": - version "7.5.8" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" - integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== - "@types/source-list-map@*": version "0.1.6" resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.6.tgz#164e169dd061795b50b83c19e4d3be09f8d3a454" @@ -1558,10 +1481,10 @@ dependencies: source-map "^0.6.1" -"@types/webpack-livereload-plugin@2.3.3": - version "2.3.3" - resolved "https://registry.yarnpkg.com/@types/webpack-livereload-plugin/-/webpack-livereload-plugin-2.3.3.tgz#96f34133f1e1515571233a3e5099d863dc8723e7" - integrity sha512-R8P2HG2mAHY3Qptspt0il8zYVNqiEeOmMe2cGFcEjH7qnJZ4uC7hujLwfAm6jrIO7I5uEs6CxfpKSXM9ULAggw== +"@types/webpack-livereload-plugin@2.3.6": + version "2.3.6" + resolved "https://registry.yarnpkg.com/@types/webpack-livereload-plugin/-/webpack-livereload-plugin-2.3.6.tgz#2c3ccefc8858525f40aeb8be0f784d5027144e23" + integrity sha512-H8nZSOWSiY/6kCpOmbutZPu7Sai1xyEXo/SrXQPCymMPNBwpYWAdOsjKqr32d+IrVjnn9GGgKSYY34TEPRxJ/A== dependencies: "@types/webpack" "^4" @@ -1575,9 +1498,9 @@ source-map "^0.7.3" "@types/webpack@^4": - version "4.41.38" - resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.38.tgz#5a40ac81bdd052bf405e8bdcf3e1236f6db6dc26" - integrity sha512-oOW7E931XJU1mVfCnxCVgv8GLFL768pDO5u2Gzk82i8yTIgX6i7cntyZOkZYb/JtYM8252SN9bQp9tgkVDSsRw== + version "4.41.39" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.39.tgz#ab6feaeef8e074d0b584bbe4a4e2dc604b58eed7" + integrity sha512-otxUJvoi6FbBq/64gGH34eblpKLgdi+gf08GaAh8Bx6So0ZZic028Ev/SUxD22gbthMKCkeeiXEat1kHLDJfYg== dependencies: "@types/node" "*" "@types/tapable" "^1" @@ -1586,98 +1509,93 @@ anymatch "^3.0.0" source-map "^0.6.0" -"@typescript-eslint/eslint-plugin@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3" - integrity sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA== +"@typescript-eslint/eslint-plugin@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.1.tgz#992e5ac1553ce20d0d46aa6eccd79dc36dedc805" + integrity sha512-Ncvsq5CT3Gvh+uJG0Lwlho6suwDfUXH0HztslDf5I+F2wAFAZMRwYLEorumpKLzmO2suAXZ/td1tBg4NZIi9CQ== dependencies: - "@eslint-community/regexpp" "^4.5.1" - "@typescript-eslint/scope-manager" "6.21.0" - "@typescript-eslint/type-utils" "6.21.0" - "@typescript-eslint/utils" "6.21.0" - "@typescript-eslint/visitor-keys" "6.21.0" - debug "^4.3.4" + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "8.18.1" + "@typescript-eslint/type-utils" "8.18.1" + "@typescript-eslint/utils" "8.18.1" + "@typescript-eslint/visitor-keys" "8.18.1" graphemer "^1.4.0" - ignore "^5.2.4" + ignore "^5.3.1" natural-compare "^1.4.0" - semver "^7.5.4" - ts-api-utils "^1.0.1" + ts-api-utils "^1.3.0" -"@typescript-eslint/parser@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b" - integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== +"@typescript-eslint/parser@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.18.1.tgz#c258bae062778b7696793bc492249027a39dfb95" + integrity sha512-rBnTWHCdbYM2lh7hjyXqxk70wvon3p2FyaniZuey5TrcGBpfhVp0OxOa6gxr9Q9YhZFKyfbEnxc24ZnVbbUkCA== dependencies: - "@typescript-eslint/scope-manager" "6.21.0" - "@typescript-eslint/types" "6.21.0" - "@typescript-eslint/typescript-estree" "6.21.0" - "@typescript-eslint/visitor-keys" "6.21.0" + "@typescript-eslint/scope-manager" "8.18.1" + "@typescript-eslint/types" "8.18.1" + "@typescript-eslint/typescript-estree" "8.18.1" + "@typescript-eslint/visitor-keys" "8.18.1" debug "^4.3.4" -"@typescript-eslint/scope-manager@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1" - integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg== +"@typescript-eslint/scope-manager@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.18.1.tgz#52cedc3a8178d7464a70beffed3203678648e55b" + integrity sha512-HxfHo2b090M5s2+/9Z3gkBhI6xBH8OJCFjH9MhQ+nnoZqxU3wNxkLT+VWXWSFWc3UF3Z+CfPAyqdCTdoXtDPCQ== dependencies: - "@typescript-eslint/types" "6.21.0" - "@typescript-eslint/visitor-keys" "6.21.0" + "@typescript-eslint/types" "8.18.1" + "@typescript-eslint/visitor-keys" "8.18.1" -"@typescript-eslint/type-utils@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e" - integrity sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag== +"@typescript-eslint/type-utils@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.18.1.tgz#10f41285475c0bdee452b79ff7223f0e43a7781e" + integrity sha512-jAhTdK/Qx2NJPNOTxXpMwlOiSymtR2j283TtPqXkKBdH8OAMmhiUfP0kJjc/qSE51Xrq02Gj9NY7MwK+UxVwHQ== dependencies: - "@typescript-eslint/typescript-estree" "6.21.0" - "@typescript-eslint/utils" "6.21.0" + "@typescript-eslint/typescript-estree" "8.18.1" + "@typescript-eslint/utils" "8.18.1" debug "^4.3.4" - ts-api-utils "^1.0.1" + ts-api-utils "^1.3.0" -"@typescript-eslint/types@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" - integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== +"@typescript-eslint/types@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.18.1.tgz#d7f4f94d0bba9ebd088de840266fcd45408a8fff" + integrity sha512-7uoAUsCj66qdNQNpH2G8MyTFlgerum8ubf21s3TSM3XmKXuIn+H2Sifh/ES2nPOPiYSRJWAk0fDkW0APBWcpfw== -"@typescript-eslint/typescript-estree@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46" - integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ== +"@typescript-eslint/typescript-estree@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.1.tgz#2a86cd64b211a742f78dfa7e6f4860413475367e" + integrity sha512-z8U21WI5txzl2XYOW7i9hJhxoKKNG1kcU4RzyNvKrdZDmbjkmLBo8bgeiOJmA06kizLI76/CCBAAGlTlEeUfyg== dependencies: - "@typescript-eslint/types" "6.21.0" - "@typescript-eslint/visitor-keys" "6.21.0" + "@typescript-eslint/types" "8.18.1" + "@typescript-eslint/visitor-keys" "8.18.1" debug "^4.3.4" - globby "^11.1.0" + fast-glob "^3.3.2" is-glob "^4.0.3" - minimatch "9.0.3" - semver "^7.5.4" - ts-api-utils "^1.0.1" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^1.3.0" -"@typescript-eslint/utils@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134" - integrity sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ== +"@typescript-eslint/utils@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.18.1.tgz#c4199ea23fc823c736e2c96fd07b1f7235fa92d5" + integrity sha512-8vikiIj2ebrC4WRdcAdDcmnu9Q/MXXwg+STf40BVfT8exDqBCUPdypvzcUPxEqRGKg9ALagZ0UWcYCtn+4W2iQ== dependencies: "@eslint-community/eslint-utils" "^4.4.0" - "@types/json-schema" "^7.0.12" - "@types/semver" "^7.5.0" - "@typescript-eslint/scope-manager" "6.21.0" - "@typescript-eslint/types" "6.21.0" - "@typescript-eslint/typescript-estree" "6.21.0" - semver "^7.5.4" + "@typescript-eslint/scope-manager" "8.18.1" + "@typescript-eslint/types" "8.18.1" + "@typescript-eslint/typescript-estree" "8.18.1" -"@typescript-eslint/visitor-keys@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47" - integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A== +"@typescript-eslint/visitor-keys@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.1.tgz#344b4f6bc83f104f514676facf3129260df7610a" + integrity sha512-Vj0WLm5/ZsD013YeUKn+K0y8p1M0jPpxOkKdbD1wB0ns53a5piVY02zjf072TblEweAbcYiFiPoSMF3kp+VhhQ== dependencies: - "@typescript-eslint/types" "6.21.0" - eslint-visitor-keys "^3.4.1" + "@typescript-eslint/types" "8.18.1" + eslint-visitor-keys "^4.2.0" "@ungap/structured-clone@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== -"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.11.5": +"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== @@ -1743,7 +1661,7 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== -"@webassemblyjs/wasm-edit@^1.11.5": +"@webassemblyjs/wasm-edit@^1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz#9f9f3ff52a14c980939be0ef9d5df9ebc678ae3b" integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g== @@ -1778,7 +1696,7 @@ "@webassemblyjs/wasm-gen" "1.12.1" "@webassemblyjs/wasm-parser" "1.12.1" -"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.11.5": +"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz#c47acb90e6f083391e3fa61d113650eea1e95937" integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== @@ -1830,10 +1748,10 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" -acorn-import-assertions@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" - integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== +acorn-import-attributes@^1.9.5: + version "1.9.5" + resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== acorn-jsx@^5.3.2: version "5.3.2" @@ -1846,9 +1764,9 @@ acorn@^6.0.6: integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: - version "8.11.3" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" - integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + version "8.12.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" + integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== add-px-to-style@1.0.0: version "1.0.0" @@ -1893,50 +1811,24 @@ ajv@^6.12.4, ajv@^6.12.5: uri-js "^4.2.2" ajv@^8.0.0, ajv@^8.0.1, ajv@^8.9.0: - version "8.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" - integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== dependencies: - fast-deep-equal "^3.1.1" + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" json-schema-traverse "^1.0.0" require-from-string "^2.0.2" - uri-js "^4.2.2" - -ansi-cyan@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-cyan/-/ansi-cyan-0.1.1.tgz#538ae528af8982f28ae30d86f2f17456d2609873" - integrity sha512-eCjan3AVo/SxZ0/MyIYRtkpxIu/H3xZN7URr1vXVrISxeyz8fUFz0FJziamK4sS8I+t35y4rHg1b2PklyBe/7A== - dependencies: - ansi-wrap "0.1.0" - -ansi-gray@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" - integrity sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw== - dependencies: - ansi-wrap "0.1.0" - -ansi-red@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-red/-/ansi-red-0.1.1.tgz#8c638f9d1080800a353c9c28c8a81ca4705d946c" - integrity sha512-ewaIr5y+9CUTGFwZfpECUbFlGcC0GCw1oqR9RI6h1gQCd9Aj2GxSckCnPsVJnmfMZbwFYE+leZGASgkWl06Jow== - dependencies: - ansi-wrap "0.1.0" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA== +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== ansi-styles@^3.2.1: version "3.2.1" @@ -1952,10 +1844,10 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" -ansi-wrap@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" - integrity sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw== +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== anymatch@^3.0.0, anymatch@^3.1.1, anymatch@~3.1.2: version "3.1.3" @@ -2025,24 +1917,6 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -arr-diff@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-1.1.0.tgz#687c32758163588fef7de7b36fabe495eb1a399a" - integrity sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q== - dependencies: - arr-flatten "^1.0.1" - array-slice "^0.2.3" - -arr-flatten@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-2.1.0.tgz#20f9eab5ec70f5c7d215b1077b1c39161d292c7d" - integrity sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA== - array-buffer-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" @@ -2056,7 +1930,7 @@ array-flatten@^2.1.0: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== -array-includes@^3.1.6, array-includes@^3.1.7: +array-includes@^3.1.6, array-includes@^3.1.8: version "3.1.8" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d" integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== @@ -2068,17 +1942,12 @@ array-includes@^3.1.6, array-includes@^3.1.7: get-intrinsic "^1.2.4" is-string "^1.0.7" -array-slice@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5" - integrity sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q== - array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array.prototype.findlast@^1.2.4: +array.prototype.findlast@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ== @@ -2090,7 +1959,7 @@ array.prototype.findlast@^1.2.4: es-object-atoms "^1.0.0" es-shim-unscopables "^1.0.2" -array.prototype.findlastindex@^1.2.3: +array.prototype.findlastindex@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz#8c35a755c72908719453f87145ca011e39334d0d" integrity sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ== @@ -2122,25 +1991,15 @@ array.prototype.flatmap@^1.3.2: es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" -array.prototype.toreversed@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz#b989a6bf35c4c5051e1dc0325151bf8088954eba" - integrity sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA== +array.prototype.tosorted@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz#fe954678ff53034e717ea3352a03f0b0b86f7ffc" + integrity sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" - -array.prototype.tosorted@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz#c8c89348337e51b8a3c48a9227f9ce93ceedcba8" - integrity sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg== - dependencies: - call-bind "^1.0.5" + call-bind "^1.0.7" define-properties "^1.2.1" - es-abstract "^1.22.3" - es-errors "^1.1.0" + es-abstract "^1.23.3" + es-errors "^1.3.0" es-shim-unscopables "^1.0.2" arraybuffer.prototype.slice@^1.0.3: @@ -2175,20 +2034,20 @@ async@^2.6.4: lodash "^4.17.14" async@^3.2.4: - version "3.2.5" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" - integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + version "3.2.6" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== -autoprefixer@10.4.14: - version "10.4.14" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.14.tgz#e28d49902f8e759dd25b153264e862df2705f79d" - integrity sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ== +autoprefixer@10.4.20: + version "10.4.20" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.20.tgz#5caec14d43976ef42e32dcb4bd62878e96be5b3b" + integrity sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g== dependencies: - browserslist "^4.21.5" - caniuse-lite "^1.0.30001464" - fraction.js "^4.2.0" + browserslist "^4.23.3" + caniuse-lite "^1.0.30001646" + fraction.js "^4.3.7" normalize-range "^0.1.2" - picocolors "^1.0.0" + picocolors "^1.0.1" postcss-value-parser "^4.2.0" available-typed-arrays@^1.0.7: @@ -2198,10 +2057,10 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" -babel-loader@9.1.3: - version "9.1.3" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-9.1.3.tgz#3d0e01b4e69760cc694ee306fe16d358aa1c6f9a" - integrity sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw== +babel-loader@9.2.1: + version "9.2.1" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-9.2.1.tgz#04c7835db16c246dd19ba0914418f3937797587b" + integrity sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA== dependencies: find-cache-dir "^4.0.0" schema-utils "^4.0.0" @@ -2212,28 +2071,28 @@ babel-plugin-inline-classnames@2.0.1: integrity sha512-Pq/jJ6hTiGiqcMmy2d4CyJcfBDeUHOdQl1t1MDWNaSKR2RxDmShSAx4Zqz6NDmFaiinaRqF8eQoTVgSRGU+McQ== babel-plugin-polyfill-corejs2@^0.4.10: - version "0.4.10" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.10.tgz#276f41710b03a64f6467433cab72cbc2653c38b1" - integrity sha512-rpIuu//y5OX6jVU+a5BCn1R5RSZYWAl2Nar76iwaOdycqb6JPxediskWFMMl7stfwNJR4b7eiQvh5fB5TEQJTQ== + version "0.4.11" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz#30320dfe3ffe1a336c15afdcdafd6fd615b25e33" + integrity sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q== dependencies: "@babel/compat-data" "^7.22.6" - "@babel/helper-define-polyfill-provider" "^0.6.1" + "@babel/helper-define-polyfill-provider" "^0.6.2" semver "^6.3.1" -babel-plugin-polyfill-corejs3@^0.10.4: - version "0.10.4" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz#789ac82405ad664c20476d0233b485281deb9c77" - integrity sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg== +babel-plugin-polyfill-corejs3@^0.10.6: + version "0.10.6" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz#2deda57caef50f59c525aeb4964d3b2f867710c7" + integrity sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA== dependencies: - "@babel/helper-define-polyfill-provider" "^0.6.1" - core-js-compat "^3.36.1" + "@babel/helper-define-polyfill-provider" "^0.6.2" + core-js-compat "^3.38.0" babel-plugin-polyfill-regenerator@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.1.tgz#4f08ef4c62c7a7f66a35ed4c0d75e30506acc6be" - integrity sha512-JfTApdE++cgcTWjsiCQlLyFBMbTUft9ja17saCc93lgV33h4tuCVj7tlvu//qpLwaG+3yEz7/KhahGrUMkVq9g== + version "0.6.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz#addc47e240edd1da1058ebda03021f382bba785e" + integrity sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg== dependencies: - "@babel/helper-define-polyfill-provider" "^0.6.1" + "@babel/helper-define-polyfill-provider" "^0.6.2" babel-plugin-transform-react-remove-prop-types@0.4.24: version "0.4.24" @@ -2317,22 +2176,22 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.2, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== +braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: - fill-range "^7.0.1" + fill-range "^7.1.1" -browserslist@^4.14.5, browserslist@^4.21.5, browserslist@^4.22.2, browserslist@^4.23.0: - version "4.23.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" - integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== +browserslist@^4.21.10, browserslist@^4.23.3, browserslist@^4.24.0: + version "4.24.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.0.tgz#a1325fe4bc80b64fda169629fc01b3d6cecd38d4" + integrity sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A== dependencies: - caniuse-lite "^1.0.30001587" - electron-to-chromium "^1.4.668" - node-releases "^2.0.14" - update-browserslist-db "^1.0.13" + caniuse-lite "^1.0.30001663" + electron-to-chromium "^1.5.28" + node-releases "^2.0.18" + update-browserslist-db "^1.1.0" buffer-crc32@^0.2.1, buffer-crc32@^0.2.13: version "0.2.13" @@ -2400,21 +2259,10 @@ camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -caniuse-lite@^1.0.30001464, caniuse-lite@^1.0.30001587: - version "1.0.30001611" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001611.tgz#4dbe78935b65851c2d2df1868af39f709a93a96e" - integrity sha512-19NuN1/3PjA3QI8Eki55N8my4LzfkMCRLgCVfrl/slbSAchQfV0+GwjPrK3rq37As4UCLlM/DHajbKkAqbv92Q== - -chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" +caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001663: + version "1.0.30001667" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz#99fc5ea0d9c6e96897a104a8352604378377f949" + integrity sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw== chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" @@ -2433,14 +2281,14 @@ chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" -chart.js@4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.4.2.tgz#95962fa6430828ed325a480cc2d5f2b4e385ac31" - integrity sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg== +chart.js@4.4.4: + version "4.4.4" + resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.4.4.tgz#b682d2e7249f7a0cbb1b1d31c840266ae9db64b7" + integrity sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA== dependencies: "@kurkle/color" "^0.3.0" -"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3: +chokidar@^3.5.3: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -2455,15 +2303,22 @@ chart.js@4.4.2: optionalDependencies: fsevents "~2.3.2" -chrome-trace-event@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" - integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== +chokidar@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.1.tgz#4a6dff66798fb0f72a94f616abbd7e1a19f31d41" + integrity sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA== + dependencies: + readdirp "^4.0.1" -classnames@2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" - integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== +chrome-trace-event@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" + integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== + +classnames@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" + integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== clean-css@^5.2.2: version "5.3.3" @@ -2477,15 +2332,6 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -clipboard@2.0.11: - version "2.0.11" - resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.11.tgz#62180360b97dd668b6b3a84ec226975762a70be5" - integrity sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw== - dependencies: - good-listener "^1.2.2" - select "^1.1.2" - tiny-emitter "^2.0.0" - clone-deep@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" @@ -2536,11 +2382,6 @@ color-string@^0.3.0: dependencies: color-name "^1.0.0" -color-support@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - color@^0.11.0: version "0.11.4" resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764" @@ -2623,17 +2464,24 @@ copy-anything@^2.0.1: dependencies: is-what "^3.14.1" -core-js-compat@^3.31.0, core-js-compat@^3.36.1: - version "3.37.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.37.0.tgz#d9570e544163779bb4dff1031c7972f44918dc73" - integrity sha512-vYq4L+T8aS5UuFg4UwDhc7YNRWVeVZwltad9C/jV3R2LgVOpS9BDr7l/WL6BN0dbV3k1XejPTHqqEzJgsa0frA== +copy-to-clipboard@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" + integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== dependencies: - browserslist "^4.23.0" + toggle-selection "^1.0.6" -core-js@3.37.0: - version "3.37.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.37.0.tgz#d8dde58e91d156b2547c19d8a4efd5c7f6c426bb" - integrity sha512-fu5vHevQ8ZG4og+LXug8ulUtVxjOcEYvifJr7L5Bfq9GOztVqsKd9/59hUk2ZSbCrS3BqUr3EpaYGIYzq7g3Ug== +core-js-compat@^3.38.0, core-js-compat@^3.38.1: + version "3.38.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.38.1.tgz#2bc7a298746ca5a7bcb9c164bcb120f2ebc09a09" + integrity sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw== + dependencies: + browserslist "^4.23.3" + +core-js@3.39.0: + version "3.39.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.39.0.tgz#57f7647f4d2d030c32a72ea23a0555b2eaa30f83" + integrity sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g== core-js@^2.4.0: version "2.6.12" @@ -2687,7 +2535,7 @@ create-react-context@^0.3.0: gud "^1.0.0" warning "^4.0.3" -cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -2707,9 +2555,9 @@ css-color-function@~1.3.3: rgb "~0.1.0" css-functions-list@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/css-functions-list/-/css-functions-list-3.2.1.tgz#2eb205d8ce9f9ce74c5c1d7490b66b77c45ce3ea" - integrity sha512-Nj5YcaGgBtuUmn1D7oHqPW0c9iui7xsTsj5lIX8ZgevdfhmjFfKB3r8moHJtNJnctnYXJyYX5I1pp90HM4TPgQ== + version "3.2.3" + resolved "https://registry.yarnpkg.com/css-functions-list/-/css-functions-list-3.2.3.tgz#95652b0c24f0f59b291a9fc386041a19d4f40dbe" + integrity sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA== css-loader@6.7.3: version "6.7.3" @@ -2812,11 +2660,11 @@ debug@^3.1.0, debug@^3.2.7: ms "^2.1.1" debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== dependencies: - ms "2.1.2" + ms "^2.1.3" decamelize-keys@^1.1.0: version "1.1.1" @@ -2885,11 +2733,6 @@ del@^6.1.1: rimraf "^3.0.2" slash "^3.0.0" -delegate@^3.1.2: - version "3.2.0" - resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" - integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== - detect-node-es@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" @@ -2996,10 +2839,15 @@ dotenv@^16.0.3: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== -electron-to-chromium@^1.4.668: - version "1.4.745" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.745.tgz#9c202ce9cbf18a5b5e0ca47145fd127cc4dd2290" - integrity sha512-tRbzkaRI5gbUn5DEvF0dV4TQbMZ5CLkWeTAXmpC9IrYT+GE+x76i9p+o3RJ5l9XmdQlI1pPhVtE9uNcJJ0G0EA== +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +electron-to-chromium@^1.5.28: + version "1.5.35" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.35.tgz#1d38d386186c72b1fa6e74c3a7de5f888b503100" + integrity sha512-hOSRInrIDm0Brzp4IHW2F/VM+638qOL2CzE0DgpnGzKW27C95IqqeqgKz/hxHGnvPxvQGpHUGD5qRVC9EZY2+A== element-class@0.2.2: version "0.2.2" @@ -3011,6 +2859,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + emojis-list@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" @@ -3023,10 +2876,10 @@ end-of-stream@^1.4.1: dependencies: once "^1.4.0" -enhanced-resolve@^5.0.0, enhanced-resolve@^5.15.0: - version "5.16.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz#65ec88778083056cb32487faa9aef82ed0864787" - integrity sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA== +enhanced-resolve@^5.0.0, enhanced-resolve@^5.17.1: + version "5.17.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -3037,9 +2890,9 @@ entities@^2.0.0: integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== envinfo@^7.7.3: - version "7.12.0" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.12.0.tgz#b56723b39c2053d67ea5714f026d05d4f5cc7acd" - integrity sha512-Iw9rQJBGpJRd3rwXm9ft/JiGoAZmLxxJZELYDQoPRZ4USVhkKtIcNBPw6U+/K2mBpaqM25JSV6Yl4Az9vO2wJg== + version "7.14.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.14.0.tgz#26dac5db54418f2a4c1159153a0b2ae980838aae" + integrity sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg== errno@^0.1.1: version "0.1.8" @@ -3069,7 +2922,7 @@ error@^7.0.0: dependencies: string-template "~0.2.1" -es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2: +es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2, es-abstract@^1.23.3: version "1.23.3" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== @@ -3128,35 +2981,35 @@ es-define-property@^1.0.0: dependencies: get-intrinsic "^1.2.4" -es-errors@^1.1.0, es-errors@^1.2.1, es-errors@^1.3.0: +es-errors@^1.2.1, es-errors@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -es-iterator-helpers@^1.0.17: - version "1.0.18" - resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.18.tgz#4d3424f46b24df38d064af6fbbc89274e29ea69d" - integrity sha512-scxAJaewsahbqTYrGKJihhViaM6DDZDDoucfvzNbK0pOren1g/daDQ3IAhzn+1G14rBG7w+i5N+qul60++zlKA== +es-iterator-helpers@^1.0.19: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.1.0.tgz#f6d745d342aea214fe09497e7152170dc333a7a6" + integrity sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw== dependencies: call-bind "^1.0.7" define-properties "^1.2.1" - es-abstract "^1.23.0" + es-abstract "^1.23.3" es-errors "^1.3.0" es-set-tostringtag "^2.0.3" function-bind "^1.1.2" get-intrinsic "^1.2.4" - globalthis "^1.0.3" + globalthis "^1.0.4" has-property-descriptors "^1.0.2" has-proto "^1.0.3" has-symbols "^1.0.3" internal-slot "^1.0.7" - iterator.prototype "^1.1.2" + iterator.prototype "^1.1.3" safe-array-concat "^1.1.2" es-module-lexer@^1.2.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.0.tgz#4878fee3789ad99e065f975fdd3c645529ff0236" - integrity sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw== + version "1.5.4" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78" + integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== es-object-atoms@^1.0.0: version "1.0.0" @@ -3195,12 +3048,12 @@ es6-promise@^4.2.8: resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== -escalade@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" - integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== @@ -3224,10 +3077,10 @@ eslint-import-resolver-node@^0.3.9: is-core-module "^2.13.0" resolve "^1.22.4" -eslint-module-utils@^2.8.0: - version "2.8.1" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz#52f2404300c3bd33deece9d7372fb337cc1d7c34" - integrity sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q== +eslint-module-utils@^2.12.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz#fe4cfb948d61f49203d7b08871982b65b9af0b0b" + integrity sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg== dependencies: debug "^3.2.7" @@ -3241,27 +3094,29 @@ eslint-plugin-filenames@1.3.2: lodash.snakecase "4.1.1" lodash.upperfirst "4.3.1" -eslint-plugin-import@2.29.1: - version "2.29.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz#d45b37b5ef5901d639c15270d74d46d161150643" - integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== +eslint-plugin-import@2.31.0: + version "2.31.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz#310ce7e720ca1d9c0bb3f69adfd1c6bdd7d9e0e7" + integrity sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A== dependencies: - array-includes "^3.1.7" - array.prototype.findlastindex "^1.2.3" + "@rtsao/scc" "^1.1.0" + array-includes "^3.1.8" + array.prototype.findlastindex "^1.2.5" array.prototype.flat "^1.3.2" array.prototype.flatmap "^1.3.2" debug "^3.2.7" doctrine "^2.1.0" eslint-import-resolver-node "^0.3.9" - eslint-module-utils "^2.8.0" - hasown "^2.0.0" - is-core-module "^2.13.1" + eslint-module-utils "^2.12.0" + hasown "^2.0.2" + is-core-module "^2.15.1" is-glob "^4.0.3" minimatch "^3.1.2" - object.fromentries "^2.0.7" - object.groupby "^1.0.1" - object.values "^1.1.7" + object.fromentries "^2.0.8" + object.groupby "^1.0.3" + object.values "^1.2.0" semver "^6.3.1" + string.prototype.trimend "^1.0.8" tsconfig-paths "^3.15.0" eslint-plugin-prettier@4.2.1: @@ -3271,39 +3126,39 @@ eslint-plugin-prettier@4.2.1: dependencies: prettier-linter-helpers "^1.0.0" -eslint-plugin-react-hooks@4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" - integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== +eslint-plugin-react-hooks@4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz#c829eb06c0e6f484b3fbb85a97e57784f328c596" + integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ== -eslint-plugin-react@7.34.1: - version "7.34.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz#6806b70c97796f5bbfb235a5d3379ece5f4da997" - integrity sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw== +eslint-plugin-react@7.37.1: + version "7.37.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.1.tgz#56493d7d69174d0d828bc83afeffe96903fdadbd" + integrity sha512-xwTnwDqzbDRA8uJ7BMxPs/EXRB3i8ZfnOIp8BsxEQkT0nHPp+WWceqGgo6rKb9ctNi8GJLDT4Go5HAWELa/WMg== dependencies: - array-includes "^3.1.7" - array.prototype.findlast "^1.2.4" + array-includes "^3.1.8" + array.prototype.findlast "^1.2.5" array.prototype.flatmap "^1.3.2" - array.prototype.toreversed "^1.1.2" - array.prototype.tosorted "^1.1.3" + array.prototype.tosorted "^1.1.4" doctrine "^2.1.0" - es-iterator-helpers "^1.0.17" + es-iterator-helpers "^1.0.19" estraverse "^5.3.0" + hasown "^2.0.2" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.1.2" - object.entries "^1.1.7" - object.fromentries "^2.0.7" - object.hasown "^1.1.3" - object.values "^1.1.7" + object.entries "^1.1.8" + object.fromentries "^2.0.8" + object.values "^1.2.0" prop-types "^15.8.1" resolve "^2.0.0-next.5" semver "^6.3.1" - string.prototype.matchall "^4.0.10" + string.prototype.matchall "^4.0.11" + string.prototype.repeat "^1.0.0" -eslint-plugin-simple-import-sort@12.1.0: - version "12.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.0.tgz#8186ad55474d2f5c986a2f1bf70625a981e30d05" - integrity sha512-Y2fqAfC11TcG/WP3TrI1Gi3p3nc8XJyEOJYHyEPEGI/UAgNx6akxxlX74p7SbAQdLcgASKhj8M0GKvH3vq/+ig== +eslint-plugin-simple-import-sort@12.1.1: + version "12.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.1.tgz#e64bfdaf91c5b98a298619aa634a9f7aa43b709e" + integrity sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA== eslint-scope@5.1.1: version "5.1.1" @@ -3331,16 +3186,21 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@8.57.0: - version "8.57.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" - integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + +eslint@8.57.1: + version "8.57.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" + integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.6.1" "@eslint/eslintrc" "^2.1.4" - "@eslint/js" "8.57.0" - "@humanwhocodes/config-array" "^0.11.14" + "@eslint/js" "8.57.1" + "@humanwhocodes/config-array" "^0.13.0" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" "@ungap/structured-clone" "^1.2.0" @@ -3385,9 +3245,9 @@ espree@^9.6.0, espree@^9.6.1: eslint-visitor-keys "^3.4.1" esquery@^1.4.2: - version "1.5.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" - integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== dependencies: estraverse "^5.1.0" @@ -3428,23 +3288,6 @@ eventsource@^1.0.7: resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.1.2.tgz#bc75ae1c60209e7cb1541231980460343eaea7c2" integrity sha512-xAH3zWhgO2/3KIniEKYPr8plNSzlGINOUqYj0m0u7AB81iRw8b/3E73W6AuU+6klLbaSFmZnaETQ2lXPfAydrA== -extend-shallow@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-1.1.4.tgz#19d6bf94dfc09d76ba711f39b872d21ff4dd9071" - integrity sha512-L7AGmkO6jhDkEBBGWlLtftA80Xq8DipnrRPr0pyi7GQLXkaq9JYA4xF4z6qnadIC6euiTDKco0cGSU9muw+WTw== - dependencies: - kind-of "^1.1.0" - -fancy-log@^1.3.2: - version "1.3.3" - resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7" - integrity sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw== - dependencies: - ansi-gray "^0.1.1" - color-support "^1.1.3" - parse-node-version "^1.0.0" - time-stamp "^1.0.0" - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -3455,7 +3298,7 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== -fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.9: +fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.9, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -3476,6 +3319,11 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-uri@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.2.tgz#d78b298cf70fd3b752fd951175a3da6a7b48f024" + integrity sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row== + fastest-levenshtein@^1.0.12, fastest-levenshtein@^1.0.16: version "1.0.16" resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" @@ -3531,15 +3379,15 @@ filemanager-webpack-plugin@8.0.0: normalize-path "^3.0.0" schema-utils "^4.0.0" -filesize@10.0.7: - version "10.0.7" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-10.0.7.tgz#2237a816ee60a83fd0c3382ae70800e54eced3ad" - integrity sha512-iMRG7Qo9nayLoU3PNCiLizYtsy4W1ClrapeCwEgtiQelOAOuRJiw4QaLI+sSr8xr901dgHv+EYP2bCusGZgoiA== +filesize@10.1.6: + version "10.1.6" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-10.1.6.tgz#31194da825ac58689c0bce3948f33ce83aabd361" + integrity sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w== -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" @@ -3608,6 +3456,14 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + fork-ts-checker-webpack-plugin@8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-8.0.0.tgz#dae45dfe7298aa5d553e2580096ced79b6179504" @@ -3626,7 +3482,7 @@ fork-ts-checker-webpack-plugin@8.0.0: semver "^7.3.5" tapable "^2.2.1" -fraction.js@^4.2.0: +fraction.js@^4.3.7: version "4.3.7" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== @@ -3646,9 +3502,9 @@ fs-extra@^10.0.0, fs-extra@^10.1.0: universalify "^2.0.0" fs-monkey@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.5.tgz#fe450175f0db0d7ea758102e1d84096acb925788" - integrity sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew== + version "1.0.6" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.6.tgz#8ead082953e88d992cf3ff844faa907b26756da2" + integrity sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg== fs.realpath@^1.0.0: version "1.0.0" @@ -3665,7 +3521,7 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: +function.prototype.name@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== @@ -3729,6 +3585,18 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== +glob@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.0.tgz#6031df0d7b65eaa1ccb9b29b5ced16cea658e77e" + integrity sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g== + dependencies: + foreground-child "^3.1.0" + jackspeak "^4.0.1" + minimatch "^10.0.0" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^2.0.0" + glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -3741,16 +3609,6 @@ glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.3: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^9.2.0: - version "9.3.5" - resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.5.tgz#ca2ed8ca452781a3009685607fdf025a899dfe21" - integrity sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q== - dependencies: - fs.realpath "^1.0.0" - minimatch "^8.0.2" - minipass "^4.2.4" - path-scurry "^1.6.1" - global-modules@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" @@ -3779,12 +3637,13 @@ globals@^13.19.0: dependencies: type-fest "^0.20.2" -globalthis@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" - integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== +globalthis@^1.0.3, globalthis@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== dependencies: - define-properties "^1.1.3" + define-properties "^1.2.1" + gopd "^1.0.1" globby@^11.0.1, globby@^11.1.0: version "11.1.0" @@ -3803,13 +3662,6 @@ globjoin@^0.1.4: resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43" integrity sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg== -good-listener@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" - integrity sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw== - dependencies: - delegate "^3.1.2" - gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -3817,7 +3669,7 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -3837,13 +3689,6 @@ hard-rejection@^2.1.0: resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg== - dependencies: - ansi-regex "^2.0.0" - has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" @@ -3944,10 +3789,10 @@ html-tags@^3.3.1: resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce" integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ== -html-webpack-plugin@5.5.1: - version "5.5.1" - resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.1.tgz#826838e31b427f5f7f30971f8d8fa2422dfa6763" - integrity sha512-cTUzZ1+NqjGEKjmVgZKLMdiFg3m9MdRXkZW2OEe69WYVi5ONLMmlnSZdXzGGMOq0C8jGDrL6EWyEDDUioHO/pA== +html-webpack-plugin@5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz#50a8fa6709245608cb00e811eacecb8e0d7b7ea0" + integrity sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw== dependencies: "@types/html-minifier-terser" "^6.0.0" html-minifier-terser "^6.0.2" @@ -3970,11 +3815,6 @@ http-parser-js@>=0.5.1: resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== -https-browserify@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" - integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg== - iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" @@ -3992,10 +3832,10 @@ ieee754@^1.1.13: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^5.2.0, ignore@^5.2.4: - version "5.3.1" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" - integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== +ignore@^5.2.0, ignore@^5.2.4, ignore@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== image-size@~0.5.0: version "0.5.5" @@ -4008,9 +3848,9 @@ immediate@~3.0.5: integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== "immutable@^3.8.1 || ^4.0.0", immutable@^4.0.0: - version "4.3.5" - resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.5.tgz#f8b436e66d59f99760dc577f5c99a4fd2a5cc5a0" - integrity sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw== + version "4.3.7" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.7.tgz#c70145fc90d89fb02021e65c84eb0226e4e5a381" + integrity sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw== import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" @@ -4026,9 +3866,9 @@ import-lazy@^4.0.0: integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== import-local@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" - integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + version "3.2.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== dependencies: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" @@ -4137,12 +3977,12 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.13.0, is-core-module@^2.13.1, is-core-module@^2.5.0: - version "2.13.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" - integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== +is-core-module@^2.13.0, is-core-module@^2.15.1, is-core-module@^2.5.0: + version "2.15.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" + integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== dependencies: - hasown "^2.0.0" + hasown "^2.0.2" is-data-view@^1.0.1: version "1.0.1" @@ -4329,15 +4169,10 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== -isstream@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== - -iterator.prototype@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.2.tgz#5e29c8924f01916cb9335f1ff80619dcff22b0c0" - integrity sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w== +iterator.prototype@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.3.tgz#016c2abe0be3bbdb8319852884f60908ac62bf9c" + integrity sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ== dependencies: define-properties "^1.2.1" get-intrinsic "^1.2.1" @@ -4345,6 +4180,13 @@ iterator.prototype@^1.1.2: reflect.getprototypeof "^1.0.4" set-function-name "^2.0.1" +jackspeak@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.0.2.tgz#11f9468a3730c6ff6f56823a820d7e3be9bef015" + integrity sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw== + dependencies: + "@isaacs/cliui" "^8.0.2" + jdu@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/jdu/-/jdu-1.0.0.tgz#28f1e388501785ae0a1d93e93ed0b14dd41e51ce" @@ -4360,14 +4202,14 @@ jest-worker@^27.4.5: supports-color "^8.0.0" jiti@^1.18.2: - version "1.21.0" - resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" - integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== + version "1.21.6" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268" + integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== -jquery@3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.0.tgz#fe2c01a05da500709006d8790fe21c8a39d75612" - integrity sha512-umpJ0/k8X0MvD1ds0P9SfowREz2LenHsQaxSohMZ5OMNEU2r0tf8pdeEFTHMFxWVxKNyU9rTtK3CWzUCTKJUeQ== +jquery@3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.1.tgz#083ef98927c9a6a74d05a6af02806566d16274de" + integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -4381,15 +4223,10 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== +jsesc@^3.0.2, jsesc@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" + integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== json-buffer@3.0.1: version "3.0.1" @@ -4459,11 +4296,6 @@ keyv@^4.5.3: dependencies: json-buffer "3.0.1" -kind-of@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-1.1.0.tgz#140a3d2d41a36d2efcfa9377b62c24f8495a5c44" - integrity sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g== - kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" @@ -4574,9 +4406,9 @@ loader-utils@^2.0.0: json5 "^2.1.2" loader-utils@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.1.tgz#4fb104b599daafd82ef3e1a41fb9265f87e1f576" - integrity sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw== + version "3.3.1" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.3.1.tgz#735b9a19fd63648ca7adbd31c2327dfe281304e5" + integrity sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg== localforage@^1.8.1: version "1.10.0" @@ -4690,10 +4522,10 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" -lru-cache@^10.2.0: - version "10.2.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" - integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== +lru-cache@^11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.0.1.tgz#3a732fbfedb82c5ba7bca6564ad3f42afcb6e147" + integrity sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ== lru-cache@^5.1.1: version "5.1.1" @@ -4785,11 +4617,11 @@ merge2@^1.3.0, merge2@^1.4.1: integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== micromatch@^4.0.0, micromatch@^4.0.4, micromatch@^4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: - braces "^3.0.2" + braces "^3.0.3" picomatch "^2.3.1" mime-db@1.52.0: @@ -4827,17 +4659,18 @@ mini-create-react-context@^0.4.0: "@babel/runtime" "^7.12.1" tiny-warning "^1.0.3" -mini-css-extract-plugin@2.7.5: - version "2.7.5" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.5.tgz#afbb344977659ec0f1f6e050c7aea456b121cfc5" - integrity sha512-9HaR++0mlgom81s95vvNjxkg52n2b5s//3ZTI1EtzFb98awsLSivs2LMsVqnQ3ay0PVhqWcGNyDaTE961FOcjQ== +mini-css-extract-plugin@2.9.1: + version "2.9.1" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.1.tgz#4d184f12ce90582e983ccef0f6f9db637b4be758" + integrity sha512-+Vyi+GCCOHnrJ2VPS+6aPoXN2k2jgUzDRhTFLjjTBn23qyXJXkjUWQgTL+mXpF5/A8ixLdCc6kWsoeOjKGejKQ== dependencies: schema-utils "^4.0.0" + tapable "^2.2.1" -minimatch@9.0.3: - version "9.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== +minimatch@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b" + integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ== dependencies: brace-expansion "^2.0.1" @@ -4855,10 +4688,10 @@ minimatch@^5.1.0: dependencies: brace-expansion "^2.0.1" -minimatch@^8.0.2: - version "8.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.4.tgz#847c1b25c014d4e9a7f68aaf63dedd668a626229" - integrity sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA== +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== dependencies: brace-expansion "^2.0.1" @@ -4883,15 +4716,10 @@ minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -minipass@^4.2.4: - version "4.2.8" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a" - integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== - -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": - version "7.0.4" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" - integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== +minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== mkdirp@^0.5.6: version "0.5.6" @@ -4905,22 +4733,17 @@ mobile-detect@1.4.5: resolved "https://registry.yarnpkg.com/mobile-detect/-/mobile-detect-1.4.5.tgz#da393c3c413ca1a9bcdd9ced653c38281c0fb6ad" integrity sha512-yc0LhH6tItlvfLBugVUEtgawwFU2sIe+cSdmRJJCTMZ5GEJyLxNyC/NIOAOGk67Fa8GNpOttO3Xz/1bHpXFD/g== -moment@2.29.4: - version "2.29.4" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" - integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== +moment@2.30.1: + version "2.30.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" + integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== mousetrap@1.6.5: version "1.6.5" resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.6.5.tgz#8a766d8c272b08393d5f56074e0b5ec183485bf9" integrity sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA== -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@^2.1.1: +ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -4968,10 +4791,10 @@ node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" -node-releases@^2.0.14: - version "2.0.14" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" - integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== normalize-package-data@^2.5.0: version "2.5.0" @@ -5026,9 +4849,9 @@ object-assign@^4.1.0, object-assign@^4.1.1: integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== object-inspect@^1.13.1: - version "1.13.1" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" - integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + version "1.13.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== object-is@^1.1.5: version "1.1.6" @@ -5053,7 +4876,7 @@ object.assign@^4.1.4, object.assign@^4.1.5: has-symbols "^1.0.3" object-keys "^1.1.1" -object.entries@^1.1.7: +object.entries@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.8.tgz#bffe6f282e01f4d17807204a24f8edd823599c41" integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ== @@ -5062,7 +4885,7 @@ object.entries@^1.1.7: define-properties "^1.2.1" es-object-atoms "^1.0.0" -object.fromentries@^2.0.7: +object.fromentries@^2.0.8: version "2.0.8" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== @@ -5072,7 +4895,7 @@ object.fromentries@^2.0.7: es-abstract "^1.23.2" es-object-atoms "^1.0.0" -object.groupby@^1.0.1: +object.groupby@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e" integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== @@ -5081,16 +4904,7 @@ object.groupby@^1.0.1: define-properties "^1.2.1" es-abstract "^1.23.2" -object.hasown@^1.1.3: - version "1.1.4" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.4.tgz#e270ae377e4c120cdcb7656ce66884a6218283dc" - integrity sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg== - dependencies: - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-object-atoms "^1.0.0" - -object.values@^1.1.6, object.values@^1.1.7: +object.values@^1.1.6, object.values@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== @@ -5107,16 +4921,16 @@ once@^1.3.0, once@^1.4.0: wrappy "1" optionator@^0.9.3: - version "0.9.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" - integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== dependencies: - "@aashutoshrathi/word-wrap" "^1.2.3" deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" + word-wrap "^1.2.5" p-limit@^2.2.0: version "2.3.0" @@ -5172,6 +4986,11 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" @@ -5197,7 +5016,7 @@ parse-json@^5.0.0, parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" -parse-node-version@^1.0.0, parse-node-version@^1.0.1: +parse-node-version@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== @@ -5235,18 +5054,18 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-scurry@^1.6.1: - version "1.10.2" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.2.tgz#8f6357eb1239d5fa1da8b9f70e9c080675458ba7" - integrity sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA== +path-scurry@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580" + integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg== dependencies: - lru-cache "^10.2.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + lru-cache "^11.0.0" + minipass "^7.1.2" path-to-regexp@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" - integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + version "1.9.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.9.0.tgz#5dc0753acbf8521ca2e0f137b4578b917b10cf24" + integrity sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g== dependencies: isarray "0.0.1" @@ -5260,10 +5079,10 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.0.0, picocolors@^1.0.1, picocolors@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59" + integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" @@ -5289,17 +5108,6 @@ pkg-dir@^7.0.0: dependencies: find-up "^6.3.0" -plugin-error@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-0.1.2.tgz#3b9bb3335ccf00f425e07437e19276967da47ace" - integrity sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw== - dependencies: - ansi-cyan "^0.1.1" - ansi-red "^0.1.1" - arr-diff "^1.0.1" - arr-union "^2.0.1" - extend-shallow "^1.1.2" - popper.js@^1.14.4: version "1.16.1" resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b" @@ -5402,27 +5210,27 @@ postcss-modules-values@^4.0.0: dependencies: icss-utils "^5.0.0" -postcss-nested@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.0.1.tgz#f83dc9846ca16d2f4fa864f16e9d9f7d0961662c" - integrity sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ== +postcss-nested@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.2.0.tgz#4c2d22ab5f20b9cb61e2c5c5915950784d068131" + integrity sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ== dependencies: - postcss-selector-parser "^6.0.11" + postcss-selector-parser "^6.1.1" postcss-resolve-nested-selector@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz#29ccbc7c37dedfac304e9fff0bf1596b3f6a0e4e" - integrity sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw== + version "0.1.6" + resolved "https://registry.yarnpkg.com/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz#3d84dec809f34de020372c41b039956966896686" + integrity sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw== postcss-safe-parser@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz#bb4c29894171a94bc5c996b9a30317ef402adaa1" integrity sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ== -postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.12, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: - version "6.0.16" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz#3b88b9f5c5abd989ef4e2fc9ec8eedd34b20fb04" - integrity sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw== +postcss-selector-parser@^6.0.12, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.1.1: + version "6.1.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de" + integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" @@ -5457,14 +5265,14 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@8.4.38, postcss@^8.0.0, postcss@^8.4.19, postcss@^8.4.21, postcss@^8.4.23: - version "8.4.38" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" - integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== +postcss@8.4.47, postcss@^8.0.0, postcss@^8.4.19, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.32: + version "8.4.47" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365" + integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ== dependencies: nanoid "^3.3.7" - picocolors "^1.0.0" - source-map-js "^1.2.0" + picocolors "^1.1.0" + source-map-js "^1.2.1" postcss@^6.0.23: version "6.0.23" @@ -5534,17 +5342,10 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -qs@6.11.1: - version "6.11.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.1.tgz#6c29dff97f0c0060765911ba65cbc9764186109f" - integrity sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ== - dependencies: - side-channel "^1.0.4" - -qs@^6.4.0: - version "6.12.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.1.tgz#39422111ca7cbdb70425541cba20c7d7b216599a" - integrity sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ== +qs@6.13.0, qs@^6.4.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== dependencies: side-channel "^1.0.6" @@ -5711,11 +5512,6 @@ react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-lazyload@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/react-lazyload/-/react-lazyload-3.2.0.tgz#497bd06a6dbd7015e3376e1137a67dc47d2dd021" - integrity sha512-zJlrG8QyVZz4+xkYZH5v1w3YaP5wEFaYSUWC4CT9UXfK75IfRAIEdnyIUF+dXr3kX2MOtL1lUaZmaQZqrETwgw== - react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" @@ -5833,10 +5629,10 @@ react-virtualized@9.21.1: prop-types "^15.6.0" react-lifecycles-compat "^3.0.4" -react-window@1.8.8: - version "1.8.8" - resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.8.tgz#1b52919f009ddf91970cbdb2050a6c7be44df243" - integrity sha512-D4IiBeRtGXziZ1n0XklnFGu7h9gU684zepqyKzgPNzrsrk7xOCxni+TCckjg2Nr/DiaEEGVVmnhYSlT2rB47dQ== +react-window@1.8.10: + version "1.8.10" + resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.10.tgz#9e6b08548316814b443f7002b1cf8fd3a1bdde03" + integrity sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg== dependencies: "@babel/runtime" "^7.0.0" memoize-one ">=3.1.1 <6" @@ -5868,7 +5664,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.3.3: +readable-stream@^2.0.0, readable-stream@^2.0.5: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -5897,6 +5693,11 @@ readdir-glob@^1.1.2: dependencies: minimatch "^5.1.0" +readdirp@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.0.2.tgz#388fccb8b75665da3abffe2d8f8ed59fe74c230a" + integrity sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA== + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -5970,10 +5771,10 @@ reflect.getprototypeof@^1.0.4: globalthis "^1.0.3" which-builtin-type "^1.1.3" -regenerate-unicode-properties@^10.1.0: - version "10.1.1" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz#6b0e05489d9076b04c436f318d9b067bba459480" - integrity sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q== +regenerate-unicode-properties@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz#626e39df8c372338ea9b8028d1f99dc3fd9c3db0" + integrity sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA== dependencies: regenerate "^1.4.2" @@ -6000,33 +5801,57 @@ regenerator-transform@^0.15.2: "@babel/runtime" "^7.8.4" regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" - integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== + version "1.5.3" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz#b3ae40b1d2499b8350ab2c3fe6ef3845d3a96f42" + integrity sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ== dependencies: - call-bind "^1.0.6" + call-bind "^1.0.7" define-properties "^1.2.1" es-errors "^1.3.0" - set-function-name "^2.0.1" + set-function-name "^2.0.2" -regexpu-core@^5.3.1: - version "5.3.2" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" - integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== +regexpu-core@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.1.1.tgz#b469b245594cb2d088ceebc6369dceb8c00becac" + integrity sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw== dependencies: - "@babel/regjsgen" "^0.8.0" regenerate "^1.4.2" - regenerate-unicode-properties "^10.1.0" - regjsparser "^0.9.1" + regenerate-unicode-properties "^10.2.0" + regjsgen "^0.8.0" + regjsparser "^0.11.0" unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.1.0" -regjsparser@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" - integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== +regexpu-core@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.2.0.tgz#0e5190d79e542bf294955dccabae04d3c7d53826" + integrity sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA== dependencies: - jsesc "~0.5.0" + regenerate "^1.4.2" + regenerate-unicode-properties "^10.2.0" + regjsgen "^0.8.0" + regjsparser "^0.12.0" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" + +regjsgen@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.8.0.tgz#df23ff26e0c5b300a6470cad160a9d090c3a37ab" + integrity sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q== + +regjsparser@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.11.1.tgz#ae55c74f646db0c8fcb922d4da635e33da405149" + integrity sha512-1DHODs4B8p/mQHU9kr+jv8+wIC9mtG4eBHxWxIq5mhjE3D5oORhCc6deRKzTjs9DcfRFmj9BHSDguZklqCGFWQ== + dependencies: + jsesc "~3.0.2" + +regjsparser@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.12.0.tgz#0e846df6c6530586429377de56e0475583b088dc" + integrity sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ== + dependencies: + jsesc "~3.0.2" relateurl@^0.2.7: version "0.2.7" @@ -6059,10 +5884,10 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== -reselect@4.1.7: - version "4.1.7" - resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.7.tgz#56480d9ff3d3188970ee2b76527bd94a95567a42" - integrity sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A== +reselect@4.1.8: + version "4.1.8" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524" + integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ== reserved-words@^0.1.2: version "0.1.2" @@ -6124,12 +5949,13 @@ rgb@~0.1.0: resolved "https://registry.yarnpkg.com/rgb/-/rgb-0.1.0.tgz#be27b291e8feffeac1bd99729721bfa40fc037b5" integrity sha512-F49dXX73a92N09uQkfCp2QjwXpmJcn9/i9PvjmwsSIXUGqRLCf/yx5Q9gRxuLQTq248kakqQuc8GX/U/CxSqlA== -rimraf@4.4.1: - version "4.4.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.1.tgz#bd33364f67021c5b79e93d7f4fa0568c7c21b755" - integrity sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og== +rimraf@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-6.0.1.tgz#ffb8ad8844dd60332ab15f52bc104bc3ed71ea4e" + integrity sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A== dependencies: - glob "^9.2.0" + glob "^11.0.0" + package-json-from-dist "^1.0.0" rimraf@^3.0.2: version "3.0.2" @@ -6145,15 +5971,6 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -run-sequence@2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/run-sequence/-/run-sequence-2.2.1.tgz#1ce643da36fd8c7ea7e1a9329da33fc2b8898495" - integrity sha512-qkzZnQWMZjcKbh3CNly2srtrkaO/2H/SI5f2eliMCapdRD3UhMrwjfOAZJAnZ2H8Ju4aBzFZkBGXUqFs9V0yxw== - dependencies: - chalk "^1.1.3" - fancy-log "^1.3.2" - plugin-error "^0.1.2" - safe-array-concat@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" @@ -6194,18 +6011,18 @@ safe-regex-test@^1.0.3: integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sass@^1.58.3: - version "1.75.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.75.0.tgz#91bbe87fb02dfcc34e052ddd6ab80f60d392be6c" - integrity sha512-ShMYi3WkrDWxExyxSZPst4/okE9ts46xZmJDSawJQrnte7M1V9fScVB+uNXOVKRBt0PggHOwoZcn8mYX4trnBw== + version "1.79.4" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.79.4.tgz#f9c45af35fbeb53d2c386850ec842098d9935267" + integrity sha512-K0QDSNPXgyqO4GZq2HO5Q70TLxTH6cIT59RdoCHMivrC8rqzaTw5ab9prjz9KUN1El4FLXrBXJhik61JR4HcGg== dependencies: - chokidar ">=3.0.0 <4.0.0" + chokidar "^4.0.0" immutable "^4.0.0" source-map-js ">=0.6.2 <2.0.0" sax@^1.2.4: - version "1.3.0" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" - integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== + version "1.4.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" + integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== sax@~1.2.4: version "1.2.4" @@ -6249,11 +6066,6 @@ section-iterator@^2.0.0: resolved "https://registry.yarnpkg.com/section-iterator/-/section-iterator-2.0.0.tgz#bf444d7afeeb94ad43c39ad2fb26151627ccba2a" integrity sha512-xvTNwcbeDayXotnV32zLb3duQsP+4XosHpb/F+tu6VzEZFmIjzPdNk6/O+QOOx5XTh08KL2ufdXeCO33p380pQ== -select@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" - integrity sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA== - "semver@2 || 3 || 4 || 5", semver@^5.6.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" @@ -6264,12 +6076,10 @@ semver@^6.0.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.4, semver@^7.3.5, semver@^7.3.8, semver@^7.5.4: - version "7.6.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" - integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== - dependencies: - lru-cache "^6.0.0" +semver@^7.3.4, semver@^7.3.5, semver@^7.3.8, semver@^7.6.0: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== serialize-javascript@^6.0.1: version "6.0.2" @@ -6358,10 +6168,10 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" -"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2, source-map-js@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" - integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2, source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== source-map-support@~0.5.20: version "0.5.21" @@ -6381,7 +6191,7 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.7.3: +source-map@^0.7.3, source-map@^0.7.4: version "0.7.4" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== @@ -6408,9 +6218,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.17" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz#887da8aa73218e51a1d917502d79863161a93f9c" - integrity sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg== + version "3.0.20" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz#e44ed19ed318dd1e5888f93325cee800f0f51b89" + integrity sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw== stack-generator@^2.0.5: version "2.0.10" @@ -6441,20 +6251,12 @@ stacktrace-js@2.0.2: stack-generator "^2.0.5" stacktrace-gps "^3.0.4" -streamqueue@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/streamqueue/-/streamqueue-1.1.2.tgz#6c99c7c20d62b57f5819296bf9ec942542380192" - integrity sha512-CHUpqa+1BM99z7clQz9W6L9ZW4eXRRQCR0H+utVAGGvNo2ePlJAFjhdK0IjunaBbY/gWKJawk5kpJeyz0EXxRA== - dependencies: - isstream "^0.1.2" - readable-stream "^2.3.3" - string-template@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" integrity sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw== -string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -6463,7 +6265,25 @@ string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string.prototype.matchall@^4.0.10: +string-width@^4.1.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string.prototype.matchall@^4.0.11: version "4.0.11" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz#1092a72c59268d2abaad76582dccc687c0297e0a" integrity sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg== @@ -6481,6 +6301,14 @@ string.prototype.matchall@^4.0.10: set-function-name "^2.0.2" side-channel "^1.0.6" +string.prototype.repeat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz#e90872ee0308b29435aa26275f6e1b762daee01a" + integrity sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + string.prototype.trim@^1.2.9: version "1.2.9" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" @@ -6528,20 +6356,27 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -strip-ansi@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -6569,12 +6404,12 @@ style-search@^0.1.0: resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" integrity sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg== -stylelint-order@6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/stylelint-order/-/stylelint-order-6.0.3.tgz#160b78650bd90463241b992581efee7159baefc2" - integrity sha512-1j1lOb4EU/6w49qZeT2SQVJXm0Ht+Qnq9GMfUa3pMwoyojIWfuA+JUDmoR97Bht1RLn4ei0xtLGy87M7d29B1w== +stylelint-order@6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/stylelint-order/-/stylelint-order-6.0.4.tgz#3e80d876c61a98d2640de181433686f24284748b" + integrity sha512-0UuKo4+s1hgQ/uAxlYU4h0o0HS4NiQDud0NAUNI0aa8FJdmYHA5ZZTFHiV5FpmE3071e9pZx5j0QpVJW5zOCUA== dependencies: - postcss "^8.4.21" + postcss "^8.4.32" postcss-sorting "^8.0.2" stylelint@15.6.1: @@ -6641,11 +6476,6 @@ sugarss@^4.0.1: resolved "https://registry.yarnpkg.com/sugarss/-/sugarss-4.0.1.tgz#128a783ed71ee0fc3b489ce1f7d5a89bc1e24383" integrity sha512-WCjS5NfuVJjkQzK10s8WOBY+hhDxxNt/N6ZaGwxFZ+wN3/lKKFSaaKUNecULcTTvE4urLcKaZFQD8vO0mOZujw== -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== - supports-color@^5.3.0, supports-color@^5.4.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -6668,9 +6498,9 @@ supports-color@^8.0.0: has-flag "^4.0.0" supports-hyperlinks@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz#c711352a5c89070779b4dad54c05a2f14b15c94b" - integrity sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA== + version "3.1.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-3.1.0.tgz#b56150ff0173baacc15f21956450b61f2b18d3ac" + integrity sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A== dependencies: has-flag "^4.0.0" supports-color "^7.0.0" @@ -6712,18 +6542,7 @@ tar-stream@^2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" -terser-webpack-plugin@5.3.9: - version "5.3.9" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1" - integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA== - dependencies: - "@jridgewell/trace-mapping" "^0.3.17" - jest-worker "^27.4.5" - schema-utils "^3.1.1" - serialize-javascript "^6.0.1" - terser "^5.16.8" - -terser-webpack-plugin@^5.3.7: +terser-webpack-plugin@5.3.10, terser-webpack-plugin@^5.3.10: version "5.3.10" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== @@ -6734,10 +6553,10 @@ terser-webpack-plugin@^5.3.7: serialize-javascript "^6.0.1" terser "^5.26.0" -terser@^5.10.0, terser@^5.16.8, terser@^5.26.0: - version "5.30.3" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.30.3.tgz#f1bb68ded42408c316b548e3ec2526d7dd03f4d2" - integrity sha512-STdUgOUx8rLbMGO9IOwHLpCqolkDITFFQSMYYwKE1N2lY6MVSaeoi10z/EhWxRc6ybqoVmKSkhKYH/XUpl7vSA== +terser@^5.10.0, terser@^5.26.0: + version "5.34.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.34.1.tgz#af40386bdbe54af0d063e0670afd55c3105abeb6" + integrity sha512-FsJZ7iZLd/BXkz+4xrRTGJ26o/6VTjQytUk8b8OxkwcD2I+79VPJlz7qss1+zE7h8GNIScFqXcDyJ/KqBYZFVA== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.8.2" @@ -6749,16 +6568,6 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -time-stamp@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" - integrity sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw== - -tiny-emitter@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" - integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== - tiny-invariant@^1.0.2: version "1.3.3" resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" @@ -6812,10 +6621,15 @@ to-space-case@^1.0.0: dependencies: to-no-case "^1.0.0" +toggle-selection@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" + integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== + "tough-cookie@^2.3.3 || ^3.0.1 || ^4.0.0": - version "4.1.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" - integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== + version "4.1.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" + integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== dependencies: psl "^1.1.33" punycode "^2.1.1" @@ -6832,20 +6646,21 @@ trim-newlines@^3.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -ts-api-utils@^1.0.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" - integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== +ts-api-utils@^1.3.0: + version "1.4.3" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064" + integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw== -ts-loader@9.4.2: - version "9.4.2" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.4.2.tgz#80a45eee92dd5170b900b3d00abcfa14949aeb78" - integrity sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA== +ts-loader@9.5.1: + version "9.5.1" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.5.1.tgz#63d5912a86312f1fbe32cef0859fb8b2193d9b89" + integrity sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg== dependencies: chalk "^4.1.0" enhanced-resolve "^5.0.0" micromatch "^4.0.0" semver "^7.3.4" + source-map "^0.7.4" tsconfig-paths@^3.15.0: version "3.15.0" @@ -6867,9 +6682,9 @@ tsconfig-paths@^4.1.2: strip-bom "^3.0.0" tslib@^2.0.0, tslib@^2.0.3, tslib@^2.3.0: - version "2.6.2" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" - integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + version "2.7.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" @@ -6969,10 +6784,10 @@ typescript-plugin-css-modules@5.0.1: stylus "^0.59.0" tsconfig-paths "^4.1.2" -typescript@5.1.6: - version "5.1.6" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" - integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== +typescript@5.7.2: + version "5.7.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6" + integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg== unbox-primitive@^1.0.2: version "1.0.2" @@ -6984,15 +6799,15 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici-types@~6.19.2: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== unicode-canonical-property-names-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" - integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + version "2.0.1" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz#cb3173fe47ca743e228216e4a3ddc4c84d628cc2" + integrity sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg== unicode-match-property-ecmascript@^2.0.0: version "2.0.0" @@ -7003,9 +6818,9 @@ unicode-match-property-ecmascript@^2.0.0: unicode-property-aliases-ecmascript "^2.0.0" unicode-match-property-value-ecmascript@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" - integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== + version "2.2.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz#a0401aee72714598f739b68b104e4fe3a0cb3c71" + integrity sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg== unicode-property-aliases-ecmascript@^2.0.0: version "2.1.0" @@ -7022,13 +6837,13 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== -update-browserslist-db@^1.0.13: - version "1.0.13" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" - integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== +update-browserslist-db@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" + integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" + escalade "^3.2.0" + picocolors "^1.1.0" uri-js@^4.2.2: version "4.4.1" @@ -7104,10 +6919,10 @@ warning@^4.0.2, warning@^4.0.3: dependencies: loose-envify "^1.0.0" -watchpack@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.1.tgz#29308f2cac150fa8e4c92f90e0ec954a9fed7fff" - integrity sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg== +watchpack@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da" + integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" @@ -7160,34 +6975,33 @@ webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@5.89.0: - version "5.89.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.89.0.tgz#56b8bf9a34356e93a6625770006490bf3a7f32dc" - integrity sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw== +webpack@5.95.0: + version "5.95.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.95.0.tgz#8fd8c454fa60dad186fbe36c400a55848307b4c0" + integrity sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q== dependencies: - "@types/eslint-scope" "^3.7.3" - "@types/estree" "^1.0.0" - "@webassemblyjs/ast" "^1.11.5" - "@webassemblyjs/wasm-edit" "^1.11.5" - "@webassemblyjs/wasm-parser" "^1.11.5" + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" acorn "^8.7.1" - acorn-import-assertions "^1.9.0" - browserslist "^4.14.5" + acorn-import-attributes "^1.9.5" + browserslist "^4.21.10" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.15.0" + enhanced-resolve "^5.17.1" es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" - graceful-fs "^4.2.9" + graceful-fs "^4.2.11" json-parse-even-better-errors "^2.3.1" loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" schema-utils "^3.2.0" tapable "^2.1.1" - terser-webpack-plugin "^5.3.7" - watchpack "^2.4.0" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" webpack-sources "^3.2.3" websocket-driver@>=0.5.1: @@ -7224,12 +7038,12 @@ which-boxed-primitive@^1.0.2: is-symbol "^1.0.3" which-builtin-type@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.3.tgz#b1b8443707cc58b6e9bf98d32110ff0c2cbd029b" - integrity sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw== + version "1.1.4" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.4.tgz#592796260602fc3514a1b5ee7fa29319b72380c3" + integrity sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w== dependencies: - function.prototype.name "^1.1.5" - has-tostringtag "^1.0.0" + function.prototype.name "^1.1.6" + has-tostringtag "^1.0.2" is-async-function "^2.0.0" is-date-object "^1.0.5" is-finalizationregistry "^1.0.2" @@ -7238,10 +7052,10 @@ which-builtin-type@^1.1.3: is-weakref "^1.0.2" isarray "^2.0.5" which-boxed-primitive "^1.0.2" - which-collection "^1.0.1" - which-typed-array "^1.1.9" + which-collection "^1.0.2" + which-typed-array "^1.1.15" -which-collection@^1.0.1: +which-collection@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== @@ -7251,7 +7065,7 @@ which-collection@^1.0.1: is-weakmap "^2.0.2" is-weakset "^2.0.3" -which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.9: +which-typed-array@^1.1.14, which-typed-array@^1.1.15: version "1.1.15" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== @@ -7281,6 +7095,29 @@ wildcard@^2.0.0: resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -7295,9 +7132,9 @@ write-file-atomic@^5.0.1: signal-exit "^4.0.1" ws@^7.4.5: - version "7.5.9" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" - integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== xxhashjs@~0.2.2: version "0.2.2" @@ -7332,9 +7169,9 @@ yocto-queue@^0.1.0: integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== yocto-queue@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" - integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== + version "1.1.1" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110" + integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g== zip-stream@^4.1.0: version "4.1.1"