mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2026-04-17 21:44:48 -04:00
Compare commits
59 Commits
v1.14.3.43
...
v1.16.1.44
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94634234ff | ||
|
|
a48d6029d9 | ||
|
|
9cc150b105 | ||
|
|
6a97d99876 | ||
|
|
c957168040 | ||
|
|
61bc35b3fa | ||
|
|
a84210c452 | ||
|
|
8af6ea1d8f | ||
|
|
1a894ac583 | ||
|
|
4f6e05414c | ||
|
|
5096a088d4 | ||
|
|
6581bddba3 | ||
|
|
292af28d42 | ||
|
|
37a6d03d52 | ||
|
|
fe35d450f0 | ||
|
|
6a9e27bc06 | ||
|
|
a989bf82ea | ||
|
|
ccc8d8002f | ||
|
|
eaaf8db486 | ||
|
|
c32fa7a84b | ||
|
|
57e21a78ee | ||
|
|
9cdf5d18d8 | ||
|
|
41b0a1211b | ||
|
|
1b8f09f2ce | ||
|
|
2f85de6b69 | ||
|
|
b2ef9d5b0a | ||
|
|
c80262d75b | ||
|
|
2a312d93ec | ||
|
|
e09df2fff3 | ||
|
|
f0c7d13b20 | ||
|
|
4dac60bef9 | ||
|
|
5aefb46790 | ||
|
|
41b043e551 | ||
|
|
5447fad1fc | ||
|
|
6a1e01abbd | ||
|
|
2803ad5ba0 | ||
|
|
8fa8a13036 | ||
|
|
41ce79ccce | ||
|
|
14ae062db2 | ||
|
|
d55a38da4a | ||
|
|
ab289cfc86 | ||
|
|
12671e9905 | ||
|
|
a33023b8c6 | ||
|
|
a3e134ce0b | ||
|
|
ee7c821cab | ||
|
|
ee4cf93aee | ||
|
|
2cacfba81f | ||
|
|
02e420580e | ||
|
|
d99398d3f8 | ||
|
|
9ea8335aa0 | ||
|
|
52a91a50b2 | ||
|
|
680bf46e25 | ||
|
|
d279c97f15 | ||
|
|
7d5d338c8e | ||
|
|
721ae1cac0 | ||
|
|
3881c9d753 | ||
|
|
131b344119 | ||
|
|
d226e52881 | ||
|
|
583815b4f7 |
19
.devcontainer/devcontainer.json
Normal file
19
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,19 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/dotnet
|
||||
{
|
||||
"name": "Prowlarr",
|
||||
"image": "mcr.microsoft.com/devcontainers/dotnet:1-6.0",
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
"nodeGypDependencies": true,
|
||||
"version": "16",
|
||||
"nvmVersion": "latest"
|
||||
}
|
||||
},
|
||||
"forwardPorts": [9696],
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": ["esbenp.prettier-vscode"]
|
||||
}
|
||||
}
|
||||
}
|
||||
12
.github/dependabot.yml
vendored
Normal file
12
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for more information:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
# https://containers.dev/guide/dependabot
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "devcontainers"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -127,6 +127,7 @@ coverage*.xml
|
||||
coverage*.json
|
||||
setup/Output/
|
||||
*.~is
|
||||
.mono
|
||||
|
||||
# VS outout folders
|
||||
bin
|
||||
|
||||
7
.vscode/extensions.json
vendored
Normal file
7
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"esbenp.prettier-vscode",
|
||||
"ms-dotnettools.csdevkit",
|
||||
"ms-vscode-remote.remote-containers"
|
||||
]
|
||||
}
|
||||
26
.vscode/launch.json
vendored
Normal file
26
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||
// Use hover for the description of the existing attributes
|
||||
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md
|
||||
"name": "Run Prowlarr",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build dotnet",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/_output/net6.0/Prowlarr",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
"request": "attach"
|
||||
}
|
||||
]
|
||||
}
|
||||
44
.vscode/tasks.json
vendored
Normal file
44
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build dotnet",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"msbuild",
|
||||
"-restore",
|
||||
"${workspaceFolder}/src/Prowlarr.sln",
|
||||
"-p:GenerateFullPaths=true",
|
||||
"-p:Configuration=Debug",
|
||||
"-p:Platform=Posix",
|
||||
"-consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "publish",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/src/Prowlarr.sln",
|
||||
"-property:GenerateFullPaths=true",
|
||||
"-consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "watch",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"watch",
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/src/Prowlarr.sln"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -9,7 +9,7 @@ variables:
|
||||
testsFolder: './_tests'
|
||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||
majorVersion: '1.14.3'
|
||||
majorVersion: '1.16.1'
|
||||
minorVersion: $[counter('minorVersion', 1)]
|
||||
prowlarrVersion: '$(majorVersion).$(minorVersion)'
|
||||
buildName: '$(Build.SourceBranchName).$(prowlarrVersion)'
|
||||
|
||||
@@ -19,7 +19,6 @@ interface SavePayload {
|
||||
seedRatio?: number;
|
||||
seedTime?: number;
|
||||
packSeedTime?: number;
|
||||
rejectBlocklistedTorrentHashesWhileGrabbing?: boolean;
|
||||
}
|
||||
|
||||
interface EditIndexerModalContentProps {
|
||||
@@ -66,10 +65,6 @@ function EditIndexerModalContent(props: EditIndexerModalContentProps) {
|
||||
const [packSeedTime, setPackSeedTime] = useState<null | string | number>(
|
||||
null
|
||||
);
|
||||
const [
|
||||
rejectBlocklistedTorrentHashesWhileGrabbing,
|
||||
setRejectBlocklistedTorrentHashesWhileGrabbing,
|
||||
] = useState(NO_CHANGE);
|
||||
|
||||
const save = useCallback(() => {
|
||||
let hasChanges = false;
|
||||
@@ -110,12 +105,6 @@ function EditIndexerModalContent(props: EditIndexerModalContentProps) {
|
||||
payload.packSeedTime = packSeedTime as number;
|
||||
}
|
||||
|
||||
if (rejectBlocklistedTorrentHashesWhileGrabbing !== NO_CHANGE) {
|
||||
hasChanges = true;
|
||||
payload.rejectBlocklistedTorrentHashesWhileGrabbing =
|
||||
rejectBlocklistedTorrentHashesWhileGrabbing === 'true';
|
||||
}
|
||||
|
||||
if (hasChanges) {
|
||||
onSavePress(payload);
|
||||
}
|
||||
@@ -129,7 +118,6 @@ function EditIndexerModalContent(props: EditIndexerModalContentProps) {
|
||||
seedRatio,
|
||||
seedTime,
|
||||
packSeedTime,
|
||||
rejectBlocklistedTorrentHashesWhileGrabbing,
|
||||
onSavePress,
|
||||
onModalClose,
|
||||
]);
|
||||
@@ -158,9 +146,6 @@ function EditIndexerModalContent(props: EditIndexerModalContentProps) {
|
||||
case 'packSeedTime':
|
||||
setPackSeedTime(value);
|
||||
break;
|
||||
case 'rejectBlocklistedTorrentHashesWhileGrabbing':
|
||||
setRejectBlocklistedTorrentHashesWhileGrabbing(value);
|
||||
break;
|
||||
default:
|
||||
console.warn(`EditIndexersModalContent Unknown Input: '${name}'`);
|
||||
}
|
||||
@@ -268,23 +253,6 @@ function EditIndexerModalContent(props: EditIndexerModalContentProps) {
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup size={sizes.MEDIUM}>
|
||||
<FormLabel>
|
||||
{translate('IndexerSettingsRejectBlocklistedTorrentHashes')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="rejectBlocklistedTorrentHashesWhileGrabbing"
|
||||
value={rejectBlocklistedTorrentHashesWhileGrabbing}
|
||||
values={enableOptions}
|
||||
helpText={translate(
|
||||
'IndexerSettingsRejectBlocklistedTorrentHashesHelpText'
|
||||
)}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter className={styles.modalFooter}>
|
||||
|
||||
@@ -47,9 +47,3 @@
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $breakpointExtraSmall) {
|
||||
.modalFooter {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +212,11 @@ class SearchFooter extends Component {
|
||||
name="searchQuery"
|
||||
value={searchQuery}
|
||||
buttons={
|
||||
<FormInputButton onPress={this.onQueryParameterModalOpenClick}>
|
||||
<FormInputButton
|
||||
kind={kinds.DEFAULT}
|
||||
onPress={this.onQueryParameterModalOpenClick}
|
||||
title={translate('ClickToChangeQueryOptions')}
|
||||
>
|
||||
<Icon
|
||||
name={icon}
|
||||
/>
|
||||
@@ -275,6 +279,7 @@ class SearchFooter extends Component {
|
||||
}
|
||||
|
||||
<SpinnerButton
|
||||
kind={kinds.PRIMARY}
|
||||
className={styles.searchButton}
|
||||
isSpinning={isFetching}
|
||||
isDisabled={isFetching || !hasIndexers}
|
||||
|
||||
@@ -17,6 +17,7 @@ import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { icons, inputTypes, kinds } from 'Helpers/Props';
|
||||
import AdvancedSettingsButton from 'Settings/AdvancedSettingsButton';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AddCategoryModalConnector from './Categories/AddCategoryModalConnector';
|
||||
import Category from './Categories/Category';
|
||||
@@ -61,6 +62,7 @@ class EditDownloadClientModalContent extends Component {
|
||||
onModalClose,
|
||||
onSavePress,
|
||||
onTestPress,
|
||||
onAdvancedSettingsPress,
|
||||
onDeleteDownloadClientPress,
|
||||
onConfirmDeleteCategory,
|
||||
...otherProps
|
||||
@@ -219,6 +221,12 @@ class EditDownloadClientModalContent extends Component {
|
||||
</Button>
|
||||
}
|
||||
|
||||
<AdvancedSettingsButton
|
||||
advancedSettings={advancedSettings}
|
||||
onAdvancedSettingsPress={onAdvancedSettingsPress}
|
||||
showLabel={false}
|
||||
/>
|
||||
|
||||
<SpinnerErrorButton
|
||||
isSpinning={isTesting}
|
||||
error={saveError}
|
||||
@@ -260,6 +268,7 @@ EditDownloadClientModalContent.propTypes = {
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
onSavePress: PropTypes.func.isRequired,
|
||||
onTestPress: PropTypes.func.isRequired,
|
||||
onAdvancedSettingsPress: PropTypes.func.isRequired,
|
||||
onDeleteDownloadClientPress: PropTypes.func,
|
||||
onConfirmDeleteCategory: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
@@ -2,7 +2,15 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { deleteDownloadClientCategory, fetchDownloadClientCategories, saveDownloadClient, setDownloadClientFieldValue, setDownloadClientValue, testDownloadClient } from 'Store/Actions/settingsActions';
|
||||
import {
|
||||
deleteDownloadClientCategory,
|
||||
fetchDownloadClientCategories,
|
||||
saveDownloadClient,
|
||||
setDownloadClientFieldValue,
|
||||
setDownloadClientValue,
|
||||
testDownloadClient,
|
||||
toggleAdvancedSettings
|
||||
} from 'Store/Actions/settingsActions';
|
||||
import createProviderSettingsSelector from 'Store/Selectors/createProviderSettingsSelector';
|
||||
import EditDownloadClientModalContent from './EditDownloadClientModalContent';
|
||||
|
||||
@@ -27,7 +35,8 @@ const mapDispatchToProps = {
|
||||
saveDownloadClient,
|
||||
testDownloadClient,
|
||||
fetchDownloadClientCategories,
|
||||
deleteDownloadClientCategory
|
||||
deleteDownloadClientCategory,
|
||||
toggleAdvancedSettings
|
||||
};
|
||||
|
||||
class EditDownloadClientModalContentConnector extends Component {
|
||||
@@ -68,6 +77,10 @@ class EditDownloadClientModalContentConnector extends Component {
|
||||
this.props.testDownloadClient({ id: this.props.id });
|
||||
};
|
||||
|
||||
onAdvancedSettingsPress = () => {
|
||||
this.props.toggleAdvancedSettings();
|
||||
};
|
||||
|
||||
onConfirmDeleteCategory = (id) => {
|
||||
this.props.deleteDownloadClientCategory({ id });
|
||||
};
|
||||
@@ -81,6 +94,7 @@ class EditDownloadClientModalContentConnector extends Component {
|
||||
{...this.props}
|
||||
onSavePress={this.onSavePress}
|
||||
onTestPress={this.onTestPress}
|
||||
onAdvancedSettingsPress={this.onAdvancedSettingsPress}
|
||||
onInputChange={this.onInputChange}
|
||||
onFieldChange={this.onFieldChange}
|
||||
onConfirmDeleteCategory={this.onConfirmDeleteCategory}
|
||||
@@ -102,6 +116,7 @@ EditDownloadClientModalContentConnector.propTypes = {
|
||||
setDownloadClientFieldValue: PropTypes.func.isRequired,
|
||||
saveDownloadClient: PropTypes.func.isRequired,
|
||||
testDownloadClient: PropTypes.func.isRequired,
|
||||
toggleAdvancedSettings: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { inputTypes, kinds } from 'Helpers/Props';
|
||||
import AdvancedSettingsButton from 'Settings/AdvancedSettingsButton';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './EditIndexerProxyModalContent.css';
|
||||
|
||||
@@ -31,6 +32,7 @@ function EditIndexerProxyModalContent(props) {
|
||||
onModalClose,
|
||||
onSavePress,
|
||||
onTestPress,
|
||||
onAdvancedSettingsPress,
|
||||
onDeleteIndexerProxyPress,
|
||||
...otherProps
|
||||
} = props;
|
||||
@@ -130,6 +132,12 @@ function EditIndexerProxyModalContent(props) {
|
||||
</Button>
|
||||
}
|
||||
|
||||
<AdvancedSettingsButton
|
||||
advancedSettings={advancedSettings}
|
||||
onAdvancedSettingsPress={onAdvancedSettingsPress}
|
||||
showLabel={false}
|
||||
/>
|
||||
|
||||
<SpinnerErrorButton
|
||||
isSpinning={isTesting}
|
||||
error={saveError}
|
||||
@@ -169,6 +177,7 @@ EditIndexerProxyModalContent.propTypes = {
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
onSavePress: PropTypes.func.isRequired,
|
||||
onTestPress: PropTypes.func.isRequired,
|
||||
onAdvancedSettingsPress: PropTypes.func.isRequired,
|
||||
onDeleteIndexerProxyPress: PropTypes.func
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,13 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { saveIndexerProxy, setIndexerProxyFieldValue, setIndexerProxyValue, testIndexerProxy } from 'Store/Actions/settingsActions';
|
||||
import {
|
||||
saveIndexerProxy,
|
||||
setIndexerProxyFieldValue,
|
||||
setIndexerProxyValue,
|
||||
testIndexerProxy,
|
||||
toggleAdvancedSettings
|
||||
} from 'Store/Actions/settingsActions';
|
||||
import createProviderSettingsSelector from 'Store/Selectors/createProviderSettingsSelector';
|
||||
import EditIndexerProxyModalContent from './EditIndexerProxyModalContent';
|
||||
|
||||
@@ -23,7 +29,8 @@ const mapDispatchToProps = {
|
||||
setIndexerProxyValue,
|
||||
setIndexerProxyFieldValue,
|
||||
saveIndexerProxy,
|
||||
testIndexerProxy
|
||||
testIndexerProxy,
|
||||
toggleAdvancedSettings
|
||||
};
|
||||
|
||||
class EditIndexerProxyModalContentConnector extends Component {
|
||||
@@ -56,6 +63,10 @@ class EditIndexerProxyModalContentConnector extends Component {
|
||||
this.props.testIndexerProxy({ id: this.props.id });
|
||||
};
|
||||
|
||||
onAdvancedSettingsPress = () => {
|
||||
this.props.toggleAdvancedSettings();
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
@@ -65,6 +76,7 @@ class EditIndexerProxyModalContentConnector extends Component {
|
||||
{...this.props}
|
||||
onSavePress={this.onSavePress}
|
||||
onTestPress={this.onTestPress}
|
||||
onAdvancedSettingsPress={this.onAdvancedSettingsPress}
|
||||
onInputChange={this.onInputChange}
|
||||
onFieldChange={this.onFieldChange}
|
||||
/>
|
||||
@@ -82,6 +94,7 @@ EditIndexerProxyModalContentConnector.propTypes = {
|
||||
setIndexerProxyFieldValue: PropTypes.func.isRequired,
|
||||
saveIndexerProxy: PropTypes.func.isRequired,
|
||||
testIndexerProxy: PropTypes.func.isRequired,
|
||||
toggleAdvancedSettings: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { inputTypes, kinds } from 'Helpers/Props';
|
||||
import AdvancedSettingsButton from 'Settings/AdvancedSettingsButton';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import NotificationEventItems from './NotificationEventItems';
|
||||
import styles from './EditNotificationModalContent.css';
|
||||
@@ -32,6 +33,7 @@ function EditNotificationModalContent(props) {
|
||||
onModalClose,
|
||||
onSavePress,
|
||||
onTestPress,
|
||||
onAdvancedSettingsPress,
|
||||
onDeleteNotificationPress,
|
||||
...otherProps
|
||||
} = props;
|
||||
@@ -136,6 +138,12 @@ function EditNotificationModalContent(props) {
|
||||
</Button>
|
||||
}
|
||||
|
||||
<AdvancedSettingsButton
|
||||
advancedSettings={advancedSettings}
|
||||
onAdvancedSettingsPress={onAdvancedSettingsPress}
|
||||
showLabel={false}
|
||||
/>
|
||||
|
||||
<SpinnerErrorButton
|
||||
isSpinning={isTesting}
|
||||
error={saveError}
|
||||
@@ -175,6 +183,7 @@ EditNotificationModalContent.propTypes = {
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
onSavePress: PropTypes.func.isRequired,
|
||||
onTestPress: PropTypes.func.isRequired,
|
||||
onAdvancedSettingsPress: PropTypes.func.isRequired,
|
||||
onDeleteNotificationPress: PropTypes.func
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,13 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { saveNotification, setNotificationFieldValue, setNotificationValue, testNotification } from 'Store/Actions/settingsActions';
|
||||
import {
|
||||
saveNotification,
|
||||
setNotificationFieldValue,
|
||||
setNotificationValue,
|
||||
testNotification,
|
||||
toggleAdvancedSettings
|
||||
} from 'Store/Actions/settingsActions';
|
||||
import createProviderSettingsSelector from 'Store/Selectors/createProviderSettingsSelector';
|
||||
import EditNotificationModalContent from './EditNotificationModalContent';
|
||||
|
||||
@@ -23,7 +29,8 @@ const mapDispatchToProps = {
|
||||
setNotificationValue,
|
||||
setNotificationFieldValue,
|
||||
saveNotification,
|
||||
testNotification
|
||||
testNotification,
|
||||
toggleAdvancedSettings
|
||||
};
|
||||
|
||||
class EditNotificationModalContentConnector extends Component {
|
||||
@@ -56,6 +63,10 @@ class EditNotificationModalContentConnector extends Component {
|
||||
this.props.testNotification({ id: this.props.id });
|
||||
};
|
||||
|
||||
onAdvancedSettingsPress = () => {
|
||||
this.props.toggleAdvancedSettings();
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
@@ -65,6 +76,7 @@ class EditNotificationModalContentConnector extends Component {
|
||||
{...this.props}
|
||||
onSavePress={this.onSavePress}
|
||||
onTestPress={this.onTestPress}
|
||||
onAdvancedSettingsPress={this.onAdvancedSettingsPress}
|
||||
onInputChange={this.onInputChange}
|
||||
onFieldChange={this.onFieldChange}
|
||||
/>
|
||||
@@ -82,6 +94,7 @@ EditNotificationModalContentConnector.propTypes = {
|
||||
setNotificationFieldValue: PropTypes.func.isRequired,
|
||||
saveNotification: PropTypes.func.isRequired,
|
||||
testNotification: PropTypes.func.isRequired,
|
||||
toggleAdvancedSettings: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
||||
@@ -41,6 +41,14 @@ function getInternalLink(source) {
|
||||
to="/settings/connect"
|
||||
/>
|
||||
);
|
||||
case 'IndexerProxyStatusCheck':
|
||||
return (
|
||||
<IconButton
|
||||
name={icons.SETTINGS}
|
||||
title={translate('Settings')}
|
||||
to="/settings/indexers"
|
||||
/>
|
||||
);
|
||||
case 'IndexerRssCheck':
|
||||
case 'IndexerSearchCheck':
|
||||
case 'IndexerStatusCheck':
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
|
||||
<meta name="description" content="Prowlarr (Preview)" />
|
||||
<meta name="description" content="Prowlarr" />
|
||||
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
|
||||
@@ -144,16 +144,46 @@
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
|
||||
<!--
|
||||
Set architecture to RuntimeInformation.ProcessArchitecture if not specified -->
|
||||
<Choose>
|
||||
<When Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture)' == 'X64'">
|
||||
<PropertyGroup>
|
||||
<Architecture>x64</Architecture>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<When Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture)' == 'X86'">
|
||||
<PropertyGroup>
|
||||
<Architecture>x86</Architecture>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<When Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture)' == 'Arm64'">
|
||||
<PropertyGroup>
|
||||
<Architecture>arm64</Architecture>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<When Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture)' == 'Arm'">
|
||||
<PropertyGroup>
|
||||
<Architecture>arm</Architecture>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<Otherwise>
|
||||
<PropertyGroup>
|
||||
<Architecture></Architecture>
|
||||
</PropertyGroup>
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
|
||||
<PropertyGroup Condition="'$(IsWindows)' == 'true' and
|
||||
'$(RuntimeIdentifier)' == ''">
|
||||
<_UsingDefaultRuntimeIdentifier>true</_UsingDefaultRuntimeIdentifier>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<RuntimeIdentifier>win-$(Architecture)</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(IsLinux)' == 'true' and
|
||||
'$(RuntimeIdentifier)' == ''">
|
||||
<_UsingDefaultRuntimeIdentifier>true</_UsingDefaultRuntimeIdentifier>
|
||||
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
|
||||
<RuntimeIdentifier>linux-$(Architecture)</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(IsOSX)' == 'true' and
|
||||
|
||||
@@ -10,6 +10,7 @@ using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http.Proxy;
|
||||
@@ -31,11 +32,14 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
private readonly ICached<System.Net.Http.HttpClient> _httpClientCache;
|
||||
private readonly ICached<CredentialCache> _credentialCache;
|
||||
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ManagedHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider,
|
||||
ICreateManagedWebProxy createManagedWebProxy,
|
||||
ICertificateValidationService certificateValidationService,
|
||||
IUserAgentBuilder userAgentBuilder,
|
||||
ICacheManager cacheManager)
|
||||
ICacheManager cacheManager,
|
||||
Logger logger)
|
||||
{
|
||||
_proxySettingsProvider = proxySettingsProvider;
|
||||
_createManagedWebProxy = createManagedWebProxy;
|
||||
@@ -44,6 +48,8 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
|
||||
_httpClientCache = cacheManager.GetCache<System.Net.Http.HttpClient>(typeof(ManagedHttpDispatcher));
|
||||
_credentialCache = cacheManager.GetCache<CredentialCache>(typeof(ManagedHttpDispatcher), "credentialcache");
|
||||
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<HttpResponse> GetResponseAsync(HttpRequest request, CookieContainer cookies)
|
||||
@@ -277,19 +283,27 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
return _credentialCache.Get("credentialCache", () => new CredentialCache());
|
||||
}
|
||||
|
||||
private static bool HasRoutableIPv4Address()
|
||||
private bool HasRoutableIPv4Address()
|
||||
{
|
||||
// Get all IPv4 addresses from all interfaces and return true if there are any with non-loopback addresses
|
||||
var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
|
||||
try
|
||||
{
|
||||
var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
|
||||
|
||||
return networkInterfaces.Any(ni =>
|
||||
ni.OperationalStatus == OperationalStatus.Up &&
|
||||
ni.GetIPProperties().UnicastAddresses.Any(ip =>
|
||||
ip.Address.AddressFamily == AddressFamily.InterNetwork &&
|
||||
!IPAddress.IsLoopback(ip.Address)));
|
||||
return networkInterfaces.Any(ni =>
|
||||
ni.OperationalStatus == OperationalStatus.Up &&
|
||||
ni.GetIPProperties().UnicastAddresses.Any(ip =>
|
||||
ip.Address.AddressFamily == AddressFamily.InterNetwork &&
|
||||
!IPAddress.IsLoopback(ip.Address)));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Debug(e, "Caught exception while GetAllNetworkInterfaces assuming IPv4 connectivity: {0}", e.Message);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static async ValueTask<Stream> onConnect(SocketsHttpConnectionContext context, CancellationToken cancellationToken)
|
||||
private async ValueTask<Stream> onConnect(SocketsHttpConnectionContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
// Until .NET supports an implementation of Happy Eyeballs (https://tools.ietf.org/html/rfc8305#section-2), let's make IPv4 fallback work in a simple way.
|
||||
// This issue is being tracked at https://github.com/dotnet/runtime/issues/26177 and expected to be fixed in .NET 6.
|
||||
@@ -313,7 +327,9 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
catch
|
||||
{
|
||||
// Do not retry IPv6 if a routable IPv4 address is available, otherwise continue to attempt IPv6 connections.
|
||||
useIPv6 = !HasRoutableIPv4Address();
|
||||
var routableIPv4 = HasRoutableIPv4Address();
|
||||
_logger.Info("IPv4 is available: {0}, IPv6 will be {1}", routableIPv4, routableIPv4 ? "disabled" : "left enabled");
|
||||
useIPv6 = !routableIPv4;
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Migration;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Migration
|
||||
{
|
||||
[TestFixture]
|
||||
public class newznab_category_to_capabilities_settingsFixture : MigrationTest<newznab_category_to_capabilities_settings>
|
||||
{
|
||||
[Test]
|
||||
public void should_migrate_categories_when_capabilities_is_not_defined()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Indexers").Row(new
|
||||
{
|
||||
Name = "Usenet Indexer",
|
||||
Redirect = false,
|
||||
AppProfileId = 0,
|
||||
DownloadClientId = 0,
|
||||
Priority = 25,
|
||||
Added = DateTime.UtcNow,
|
||||
Implementation = "Newznab",
|
||||
Settings = new
|
||||
{
|
||||
Categories = new[]
|
||||
{
|
||||
new { Id = 2000, Name = "Movies" },
|
||||
new { Id = 5000, Name = "TV" }
|
||||
}
|
||||
}.ToJson(),
|
||||
ConfigContract = "NewznabSettings"
|
||||
});
|
||||
});
|
||||
|
||||
var items = db.Query<IndexerDefinition40>("SELECT \"Id\", \"Implementation\", \"ConfigContract\", \"Settings\" FROM \"Indexers\"");
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
items.First().Implementation.Should().Be("Newznab");
|
||||
items.First().ConfigContract.Should().Be("NewznabSettings");
|
||||
items.First().Settings.Should().ContainKey("capabilities");
|
||||
items.First().Settings.Should().NotContainKey("categories");
|
||||
|
||||
var newznabSettings = items.First().Settings.ToObject<NewznabSettings40>();
|
||||
newznabSettings.Capabilities.Should().NotBeNull();
|
||||
newznabSettings.Capabilities.SupportsRawSearch.Should().Be(false);
|
||||
newznabSettings.Capabilities.Categories.Should().HaveCount(2);
|
||||
newznabSettings.Capabilities.Categories.Should().Contain(c => c.Id == 2000 && c.Name == "Movies");
|
||||
newznabSettings.Capabilities.Categories.Should().Contain(c => c.Id == 5000 && c.Name == "TV");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_migrate_categories_when_capabilities_is_defined()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Indexers").Row(new
|
||||
{
|
||||
Name = "Usenet Indexer",
|
||||
Redirect = false,
|
||||
AppProfileId = 0,
|
||||
DownloadClientId = 0,
|
||||
Priority = 25,
|
||||
Added = DateTime.UtcNow,
|
||||
Implementation = "Newznab",
|
||||
Settings = new
|
||||
{
|
||||
Capabilities = new
|
||||
{
|
||||
SupportsRawSearch = true
|
||||
},
|
||||
Categories = new[]
|
||||
{
|
||||
new { Id = 2000, Name = "Movies" },
|
||||
new { Id = 5000, Name = "TV" }
|
||||
}
|
||||
}.ToJson(),
|
||||
ConfigContract = "NewznabSettings"
|
||||
});
|
||||
});
|
||||
|
||||
var items = db.Query<IndexerDefinition40>("SELECT \"Id\", \"Implementation\", \"ConfigContract\", \"Settings\" FROM \"Indexers\"");
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
items.First().Implementation.Should().Be("Newznab");
|
||||
items.First().ConfigContract.Should().Be("NewznabSettings");
|
||||
items.First().Settings.Should().ContainKey("capabilities");
|
||||
items.First().Settings.Should().NotContainKey("categories");
|
||||
|
||||
var newznabSettings = items.First().Settings.ToObject<NewznabSettings40>();
|
||||
newznabSettings.Capabilities.Should().NotBeNull();
|
||||
newznabSettings.Capabilities.SupportsRawSearch.Should().Be(true);
|
||||
newznabSettings.Capabilities.Categories.Should().HaveCount(2);
|
||||
newznabSettings.Capabilities.Categories.Should().Contain(c => c.Id == 2000 && c.Name == "Movies");
|
||||
newznabSettings.Capabilities.Categories.Should().Contain(c => c.Id == 5000 && c.Name == "TV");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_defaults_when_categories_are_empty()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Indexers").Row(new
|
||||
{
|
||||
Name = "Usenet Indexer",
|
||||
Redirect = false,
|
||||
AppProfileId = 0,
|
||||
DownloadClientId = 0,
|
||||
Priority = 25,
|
||||
Added = DateTime.UtcNow,
|
||||
Implementation = "Newznab",
|
||||
Settings = new
|
||||
{
|
||||
Categories = Array.Empty<object>()
|
||||
}.ToJson(),
|
||||
ConfigContract = "NewznabSettings"
|
||||
});
|
||||
});
|
||||
|
||||
var items = db.Query<IndexerDefinition40>("SELECT \"Id\", \"Implementation\", \"ConfigContract\", \"Settings\" FROM \"Indexers\"");
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
items.First().Implementation.Should().Be("Newznab");
|
||||
items.First().ConfigContract.Should().Be("NewznabSettings");
|
||||
items.First().Settings.Should().ContainKey("capabilities");
|
||||
items.First().Settings.Should().NotContainKey("categories");
|
||||
|
||||
var newznabSettings = items.First().Settings.ToObject<NewznabSettings40>();
|
||||
newznabSettings.Capabilities.Should().NotBeNull();
|
||||
newznabSettings.Capabilities.SupportsRawSearch.Should().Be(false);
|
||||
newznabSettings.Capabilities.Categories.Should().NotBeNull();
|
||||
newznabSettings.Capabilities.Categories.Should().HaveCount(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_defaults_when_settings_are_empty()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Indexers").Row(new
|
||||
{
|
||||
Name = "Usenet Indexer",
|
||||
Redirect = false,
|
||||
AppProfileId = 0,
|
||||
DownloadClientId = 0,
|
||||
Priority = 25,
|
||||
Added = DateTime.UtcNow,
|
||||
Implementation = "Newznab",
|
||||
Settings = new { }.ToJson(),
|
||||
ConfigContract = "NewznabSettings"
|
||||
});
|
||||
});
|
||||
|
||||
var items = db.Query<IndexerDefinition40>("SELECT \"Id\", \"Implementation\", \"ConfigContract\", \"Settings\" FROM \"Indexers\"");
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
items.First().Implementation.Should().Be("Newznab");
|
||||
items.First().ConfigContract.Should().Be("NewznabSettings");
|
||||
items.First().Settings.Should().NotContainKey("capabilities");
|
||||
items.First().Settings.Should().NotContainKey("categories");
|
||||
items.First().Settings.ToObject<NewznabSettings40>().Capabilities.Should().BeNull();
|
||||
}
|
||||
}
|
||||
|
||||
public class IndexerDefinition40
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Implementation { get; set; }
|
||||
public string ConfigContract { get; set; }
|
||||
public JObject Settings { get; set; }
|
||||
}
|
||||
|
||||
public class NewznabSettings39
|
||||
{
|
||||
public object Categories { get; set; }
|
||||
}
|
||||
|
||||
public class NewznabSettings40
|
||||
{
|
||||
public NewznabCapabilitiesSettings40 Capabilities { get; set; }
|
||||
}
|
||||
|
||||
public class NewznabCapabilitiesSettings40
|
||||
{
|
||||
public bool SupportsRawSearch { get; set; }
|
||||
public List<IndexerCategory40> Categories { get; set; }
|
||||
}
|
||||
|
||||
public class IndexerCategory40
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ namespace NzbDrone.Core.Test.Framework
|
||||
Mocker.SetConstant<IHttpProxySettingsProvider>(new HttpProxySettingsProvider(Mocker.Resolve<ConfigService>()));
|
||||
Mocker.SetConstant<ICreateManagedWebProxy>(new ManagedWebProxyFactory(Mocker.Resolve<CacheManager>()));
|
||||
Mocker.SetConstant<ICertificateValidationService>(new X509CertificateValidationService(Mocker.Resolve<ConfigService>(), TestLogger));
|
||||
Mocker.SetConstant<IHttpDispatcher>(new ManagedHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<ICreateManagedWebProxy>(), Mocker.Resolve<ICertificateValidationService>(), Mocker.Resolve<UserAgentBuilder>(), Mocker.Resolve<CacheManager>()));
|
||||
Mocker.SetConstant<IHttpDispatcher>(new ManagedHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<ICreateManagedWebProxy>(), Mocker.Resolve<ICertificateValidationService>(), Mocker.Resolve<UserAgentBuilder>(), Mocker.Resolve<CacheManager>(), TestLogger));
|
||||
Mocker.SetConstant<IHttpClient>(new HttpClient(Array.Empty<IHttpRequestInterceptor>(), Mocker.Resolve<CacheManager>(), Mocker.Resolve<RateLimitService>(), Mocker.Resolve<IHttpDispatcher>(), TestLogger));
|
||||
Mocker.SetConstant<IProwlarrCloudRequestBuilder>(new ProwlarrCloudRequestBuilder());
|
||||
}
|
||||
|
||||
@@ -45,10 +45,10 @@ namespace NzbDrone.Core.Test.IndexerTests.AnimeBytesTests
|
||||
|
||||
var releases = (await Subject.Fetch(new BasicSearchCriteria { SearchTerm = "test", Categories = new[] { 2000, 5000 } })).Releases;
|
||||
|
||||
releases.Should().HaveCount(33);
|
||||
releases.Should().HaveCount(39);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
|
||||
var firstTorrentInfo = releases.ElementAt(2) as TorrentInfo;
|
||||
var firstTorrentInfo = releases.ElementAt(3) as TorrentInfo;
|
||||
|
||||
firstTorrentInfo.Title.Should().Be("[SubsPlease] One Piece: The Great Gold Pirate - 1059 [Web][MKV][h264][720p][AAC 2.0][Softsubs (SubsPlease)][Episode 1059]");
|
||||
firstTorrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
@@ -66,7 +66,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AnimeBytesTests
|
||||
firstTorrentInfo.Files.Should().Be(1);
|
||||
firstTorrentInfo.MinimumSeedTime.Should().Be(259200);
|
||||
|
||||
var secondTorrentInfo = releases.ElementAt(16) as TorrentInfo;
|
||||
var secondTorrentInfo = releases.ElementAt(20) as TorrentInfo;
|
||||
|
||||
secondTorrentInfo.Title.Should().Be("[GHOST] BLEACH S03 [Blu-ray][MKV][h265 10-bit][1080p][AC3 2.0][Dual Audio][Softsubs (GHOST)]");
|
||||
secondTorrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
@@ -84,7 +84,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AnimeBytesTests
|
||||
secondTorrentInfo.Files.Should().Be(22);
|
||||
secondTorrentInfo.MinimumSeedTime.Should().Be(655200);
|
||||
|
||||
var thirdTorrentInfo = releases.ElementAt(18) as TorrentInfo;
|
||||
var thirdTorrentInfo = releases.ElementAt(23) as TorrentInfo;
|
||||
|
||||
thirdTorrentInfo.Title.Should().Be("[Polarwindz] Cowboy Bebop: Tengoku no Tobira 2001 [Blu-ray][MKV][h265 10-bit][1080p][Opus 5.1][Softsubs (Polarwindz)]");
|
||||
thirdTorrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
@@ -102,7 +102,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AnimeBytesTests
|
||||
thirdTorrentInfo.Files.Should().Be(1);
|
||||
thirdTorrentInfo.MinimumSeedTime.Should().Be(475200);
|
||||
|
||||
var fourthTorrentInfo = releases.ElementAt(3) as TorrentInfo;
|
||||
var fourthTorrentInfo = releases.ElementAt(5) as TorrentInfo;
|
||||
|
||||
fourthTorrentInfo.Title.Should().Be("[SubsPlease] Dr. STONE: NEW WORLD S03E03 - 03 [Web][MKV][h264][720p][AAC 2.0][Softsubs (SubsPlease)][Episode 3]");
|
||||
fourthTorrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
@@ -120,7 +120,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AnimeBytesTests
|
||||
fourthTorrentInfo.Files.Should().Be(1);
|
||||
fourthTorrentInfo.MinimumSeedTime.Should().Be(259200);
|
||||
|
||||
var fifthTorrentInfo = releases.ElementAt(23) as TorrentInfo;
|
||||
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.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
@@ -138,7 +138,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AnimeBytesTests
|
||||
fifthTorrentInfo.Files.Should().Be(11);
|
||||
fifthTorrentInfo.MinimumSeedTime.Should().Be(529200);
|
||||
|
||||
var sixthTorrentInfo = releases.ElementAt(31) as TorrentInfo;
|
||||
var sixthTorrentInfo = releases.ElementAt(37) as TorrentInfo;
|
||||
|
||||
sixthTorrentInfo.Title.Should().Be("[HorribleSubs] Dr. STONE S01 [Web][MKV][h264][720p][AAC 2.0][Softsubs (HorribleSubs)]");
|
||||
sixthTorrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AvistazTests
|
||||
torrentInfo.InfoUrl.Should().Be("https://avistaz.to/torrent/187240-japan-sinks-people-of-hope-2021-s01e05-720p-nf-web-dl-ddp20-x264-seikel");
|
||||
torrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2021-11-14 22:26:21"));
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2021-11-14 21:26:21"));
|
||||
torrentInfo.Size.Should().Be(935127615);
|
||||
torrentInfo.InfoHash.Should().Be("a879261d4e6e792402f92401141a21de70d51bf2");
|
||||
torrentInfo.MagnetUrl.Should().Be(null);
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AvistazTests
|
||||
torrentInfo.InfoUrl.Should().Be("https://exoticaz.to/torrent/64040-ssis-419-my-first-experience-is-yua-mikami-from-the-day-i-lost-my-virginity-i-was-devoted-to-sex");
|
||||
torrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2022-06-11 10:04:50"));
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2022-06-11 09:04:50"));
|
||||
torrentInfo.Size.Should().Be(7085405541);
|
||||
torrentInfo.InfoHash.Should().Be("asdjfiasdf54asd7f4a2sdf544asdf");
|
||||
torrentInfo.MagnetUrl.Should().Be(null);
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AvistazTests
|
||||
torrentInfo.InfoUrl.Should().Be("https://privatehd.to/torrent/78506-godzilla-2014-2160p-uhd-bluray-remux-hdr-hevc-atmos-triton");
|
||||
torrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2021-03-21 05:24:49"));
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2021-03-21 04:24:49"));
|
||||
torrentInfo.Size.Should().Be(69914591044);
|
||||
torrentInfo.InfoHash.Should().Be("a879261d4e6e792402f92401141a21de70d51bf2");
|
||||
torrentInfo.MagnetUrl.Should().Be(null);
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
|
||||
torrentInfo.InfoUrl.Should().Be("https://filelist.io/details.php?id=665873");
|
||||
torrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2020-01-25 20:20:19"));
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2020-01-25 19:20:19"));
|
||||
torrentInfo.Size.Should().Be(8300512414);
|
||||
torrentInfo.InfoHash.Should().Be(null);
|
||||
torrentInfo.MagnetUrl.Should().Be(null);
|
||||
|
||||
@@ -33,6 +33,10 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
};
|
||||
|
||||
_caps = new IndexerCapabilities();
|
||||
|
||||
_caps.Categories.AddCategoryMapping(2000, NewznabStandardCategory.Movies, "Movies");
|
||||
_caps.Categories.AddCategoryMapping(5000, NewznabStandardCategory.TV, "TV");
|
||||
|
||||
Mocker.GetMock<INewznabCapabilitiesProvider>()
|
||||
.Setup(v => v.GetCapabilities(It.IsAny<NewznabSettings>(), It.IsAny<IndexerDefinition>()))
|
||||
.Returns(_caps);
|
||||
|
||||
@@ -37,6 +37,9 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
|
||||
|
||||
_caps.Categories.AddCategoryMapping(2000, NewznabStandardCategory.Movies, "Movies");
|
||||
_caps.Categories.AddCategoryMapping(2040, NewznabStandardCategory.MoviesHD, "Movies/HD");
|
||||
_caps.Categories.AddCategoryMapping(5000, NewznabStandardCategory.TV, "TV");
|
||||
_caps.Categories.AddCategoryMapping(5040, NewznabStandardCategory.TVHD, "TV/HD");
|
||||
_caps.Categories.AddCategoryMapping(5070, NewznabStandardCategory.TVAnime, "TV/Anime");
|
||||
|
||||
Mocker.GetMock<INewznabCapabilitiesProvider>()
|
||||
.Setup(v => v.GetCapabilities(It.IsAny<NewznabSettings>(), It.IsAny<IndexerDefinition>()))
|
||||
@@ -83,23 +86,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
|
||||
|
||||
var releases = (await Subject.Fetch(new MovieSearchCriteria())).Releases;
|
||||
|
||||
releases.Should().HaveCount(5);
|
||||
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
var releaseInfo = releases.First() as TorrentInfo;
|
||||
|
||||
releaseInfo.Title.Should().Be("Series Title S05E02 HDTV x264-Xclusive [eztv]");
|
||||
releaseInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
releaseInfo.MagnetUrl.Should().Be("magnet:?xt=urn:btih:9fb267cff5ae5603f07a347676ec3bf3e35f75e1&dn=Game+of+Thrones+S05E02+HDTV+x264-Xclusive+%5Beztv%5D&tr=udp:%2F%2Fopen.demonii.com:1337&tr=udp:%2F%2Ftracker.coppersurfer.tk:6969&tr=udp:%2F%2Ftracker.leechers-paradise.org:6969&tr=udp:%2F%2Fexodus.desync.com:6969");
|
||||
releaseInfo.DownloadUrl.Should().Be("magnet:?xt=urn:btih:9fb267cff5ae5603f07a347676ec3bf3e35f75e1&dn=Game+of+Thrones+S05E02+HDTV+x264-Xclusive+%5Beztv%5D&tr=udp:%2F%2Fopen.demonii.com:1337&tr=udp:%2F%2Ftracker.coppersurfer.tk:6969&tr=udp:%2F%2Ftracker.leechers-paradise.org:6969&tr=udp:%2F%2Fexodus.desync.com:6969");
|
||||
releaseInfo.InfoUrl.Should().Be("https://thepiratebay.se/torrent/11811366/Series_Title_S05E02_HDTV_x264-Xclusive_%5Beztv%5D");
|
||||
releaseInfo.CommentUrl.Should().Be("https://thepiratebay.se/torrent/11811366/Series_Title_S05E02_HDTV_x264-Xclusive_%5Beztv%5D");
|
||||
releaseInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
releaseInfo.PublishDate.Should().Be(DateTime.Parse("Sat, 11 Apr 2015 21:34:00 -0600").ToUniversalTime());
|
||||
releaseInfo.Size.Should().Be(388895872);
|
||||
releaseInfo.InfoHash.Should().Be("9fb267cff5ae5603f07a347676ec3bf3e35f75e1");
|
||||
releaseInfo.Seeders.Should().Be(34128);
|
||||
releaseInfo.Peers.Should().Be(36724);
|
||||
releases.Should().HaveCount(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -13,6 +13,6 @@ namespace NzbDrone.Core.Applications
|
||||
|
||||
public override bool SendUpdatesToClient => true;
|
||||
|
||||
public override string CompletionMessage => null;
|
||||
public override string CompletionMessage => "Completed";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,14 @@ 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, Logger logger)
|
||||
public LazyLibrarian(ILazyLibrarianV1Proxy lazyLibrarianV1Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, IIndexerFactory indexerFactory, Logger logger)
|
||||
: base(appIndexerMapService, logger)
|
||||
{
|
||||
_lazyLibrarianV1Proxy = lazyLibrarianV1Proxy;
|
||||
_configFileProvider = configFileProvider;
|
||||
_indexerFactory = indexerFactory;
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
@@ -65,7 +67,9 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
|
||||
public override void AddIndexer(IndexerDefinition indexer)
|
||||
{
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty())
|
||||
var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities();
|
||||
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty())
|
||||
{
|
||||
_logger.Trace("Skipping add for indexer {0} [{1}] due to no app Sync Categories supported by the indexer", indexer.Name, indexer.Id);
|
||||
|
||||
@@ -74,7 +78,7 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
|
||||
_logger.Trace("Adding indexer {0} [{1}]", indexer.Name, indexer.Id);
|
||||
|
||||
var lazyLibrarianIndexer = BuildLazyLibrarianIndexer(indexer, indexer.Protocol);
|
||||
var lazyLibrarianIndexer = BuildLazyLibrarianIndexer(indexer, indexerCapabilities, indexer.Protocol);
|
||||
|
||||
var remoteIndexer = _lazyLibrarianV1Proxy.AddIndexer(lazyLibrarianIndexer, Settings);
|
||||
|
||||
@@ -107,11 +111,12 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
{
|
||||
_logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id);
|
||||
|
||||
var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities();
|
||||
var appMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id);
|
||||
var indexerMapping = appMappings.FirstOrDefault(m => m.IndexerId == indexer.Id);
|
||||
var indexerProps = indexerMapping.RemoteIndexerName.Split(",");
|
||||
|
||||
var lazyLibrarianIndexer = BuildLazyLibrarianIndexer(indexer, indexer.Protocol, indexerProps[1]);
|
||||
var lazyLibrarianIndexer = BuildLazyLibrarianIndexer(indexer, indexerCapabilities, indexer.Protocol, indexerProps[1]);
|
||||
|
||||
//Use the old remote id to find the indexer on LazyLibrarian incase the update was from a name change in Prowlarr
|
||||
var remoteIndexer = _lazyLibrarianV1Proxy.GetIndexer(indexerProps[1], lazyLibrarianIndexer.Type, Settings);
|
||||
@@ -133,7 +138,7 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
{
|
||||
_appIndexerMapService.Delete(indexerMapping.Id);
|
||||
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
{
|
||||
_logger.Debug("Remote indexer not found, re-adding {0} [{1}] to LazyLibrarian", indexer.Name, indexer.Id);
|
||||
var newRemoteIndexer = _lazyLibrarianV1Proxy.AddIndexer(lazyLibrarianIndexer, Settings);
|
||||
@@ -146,7 +151,7 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
}
|
||||
}
|
||||
|
||||
private LazyLibrarianIndexer BuildLazyLibrarianIndexer(IndexerDefinition indexer, DownloadProtocol protocol, string originalName = null)
|
||||
private LazyLibrarianIndexer BuildLazyLibrarianIndexer(IndexerDefinition indexer, IndexerCapabilities indexerCapabilities, DownloadProtocol protocol, string originalName = null)
|
||||
{
|
||||
var schema = protocol == DownloadProtocol.Usenet ? LazyLibrarianProviderType.Newznab : LazyLibrarianProviderType.Torznab;
|
||||
|
||||
@@ -156,7 +161,7 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
Altername = $"{indexer.Name} (Prowlarr)",
|
||||
Host = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/api",
|
||||
Apikey = _configFileProvider.ApiKey,
|
||||
Categories = string.Join(",", indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray())),
|
||||
Categories = string.Join(",", indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray())),
|
||||
Enabled = indexer.Enable,
|
||||
Type = schema,
|
||||
Priority = indexer.Priority
|
||||
|
||||
@@ -21,13 +21,15 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
private readonly ILidarrV1Proxy _lidarrV1Proxy;
|
||||
private readonly ICached<List<LidarrIndexer>> _schemaCache;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly IIndexerFactory _indexerFactory;
|
||||
|
||||
public Lidarr(ICacheManager cacheManager, ILidarrV1Proxy lidarrV1Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, Logger logger)
|
||||
public Lidarr(ICacheManager cacheManager, ILidarrV1Proxy lidarrV1Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, IIndexerFactory indexerFactory, Logger logger)
|
||||
: base(appIndexerMapService, logger)
|
||||
{
|
||||
_schemaCache = cacheManager.GetCache<List<LidarrIndexer>>(GetType());
|
||||
_lidarrV1Proxy = lidarrV1Proxy;
|
||||
_configFileProvider = configFileProvider;
|
||||
_indexerFactory = indexerFactory;
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
@@ -49,7 +51,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
|
||||
try
|
||||
{
|
||||
failures.AddIfNotNull(_lidarrV1Proxy.TestConnection(BuildLidarrIndexer(testIndexer, DownloadProtocol.Usenet), Settings));
|
||||
failures.AddIfNotNull(_lidarrV1Proxy.TestConnection(BuildLidarrIndexer(testIndexer, testIndexer.Capabilities, DownloadProtocol.Usenet), Settings));
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
@@ -118,7 +120,9 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
|
||||
public override void AddIndexer(IndexerDefinition indexer)
|
||||
{
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty())
|
||||
var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities();
|
||||
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty())
|
||||
{
|
||||
_logger.Trace("Skipping add for indexer {0} [{1}] due to no app Sync Categories supported by the indexer", indexer.Name, indexer.Id);
|
||||
|
||||
@@ -127,7 +131,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
|
||||
_logger.Trace("Adding indexer {0} [{1}]", indexer.Name, indexer.Id);
|
||||
|
||||
var lidarrIndexer = BuildLidarrIndexer(indexer, indexer.Protocol);
|
||||
var lidarrIndexer = BuildLidarrIndexer(indexer, indexerCapabilities, indexer.Protocol);
|
||||
|
||||
var remoteIndexer = _lidarrV1Proxy.AddIndexer(lidarrIndexer, Settings);
|
||||
|
||||
@@ -159,10 +163,11 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
{
|
||||
_logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id);
|
||||
|
||||
var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities();
|
||||
var appMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id);
|
||||
var indexerMapping = appMappings.FirstOrDefault(m => m.IndexerId == indexer.Id);
|
||||
|
||||
var lidarrIndexer = BuildLidarrIndexer(indexer, indexer.Protocol, indexerMapping?.RemoteIndexerId ?? 0);
|
||||
var lidarrIndexer = BuildLidarrIndexer(indexer, indexerCapabilities, indexer.Protocol, indexerMapping?.RemoteIndexerId ?? 0);
|
||||
|
||||
var remoteIndexer = _lidarrV1Proxy.GetIndexer(indexerMapping.RemoteIndexerId, Settings);
|
||||
|
||||
@@ -174,7 +179,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
{
|
||||
_logger.Debug("Syncing remote indexer with current settings");
|
||||
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
{
|
||||
// Retain user fields not-affiliated with Prowlarr
|
||||
lidarrIndexer.Fields.AddRange(remoteIndexer.Fields.Where(f => lidarrIndexer.Fields.All(s => s.Name != f.Name)));
|
||||
@@ -200,7 +205,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
{
|
||||
_appIndexerMapService.Delete(indexerMapping.Id);
|
||||
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
{
|
||||
_logger.Debug("Remote indexer not found, re-adding {0} [{1}] to Lidarr", indexer.Name, indexer.Id);
|
||||
lidarrIndexer.Id = 0;
|
||||
@@ -214,7 +219,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
}
|
||||
}
|
||||
|
||||
private LidarrIndexer BuildLidarrIndexer(IndexerDefinition indexer, DownloadProtocol protocol, int id = 0)
|
||||
private LidarrIndexer BuildLidarrIndexer(IndexerDefinition indexer, IndexerCapabilities indexerCapabilities, DownloadProtocol protocol, int id = 0)
|
||||
{
|
||||
var cacheKey = $"{Settings.BaseUrl}";
|
||||
var schemas = _schemaCache.Get(cacheKey, () => _lidarrV1Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7));
|
||||
@@ -250,7 +255,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/";
|
||||
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api";
|
||||
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;
|
||||
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));
|
||||
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));
|
||||
|
||||
if (indexer.Protocol == DownloadProtocol.Torrent)
|
||||
{
|
||||
@@ -265,7 +270,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
|
||||
if (lidarrIndexer.Fields.Any(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing"))
|
||||
{
|
||||
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.RejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value = Settings.SyncRejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,9 +36,12 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
[FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Lidarr in Settings/General")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
|
||||
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), HelpText = "Only Indexers that support these categories will be synced", Advanced = true)]
|
||||
public IEnumerable<int> SyncCategories { get; set; }
|
||||
|
||||
[FieldDefinition(4, Type = FieldType.Checkbox, Label = "ApplicationSettingsSyncRejectBlocklistedTorrentHashes", HelpText = "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
|
||||
public bool SyncRejectBlocklistedTorrentHashesWhileGrabbing { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
||||
@@ -15,12 +15,14 @@ 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, Logger logger)
|
||||
public Mylar(IMylarV3Proxy mylarV3Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, IIndexerFactory indexerFactory, Logger logger)
|
||||
: base(appIndexerMapService, logger)
|
||||
{
|
||||
_mylarV3Proxy = mylarV3Proxy;
|
||||
_configFileProvider = configFileProvider;
|
||||
_indexerFactory = indexerFactory;
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
@@ -65,7 +67,9 @@ namespace NzbDrone.Core.Applications.Mylar
|
||||
|
||||
public override void AddIndexer(IndexerDefinition indexer)
|
||||
{
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty())
|
||||
var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities();
|
||||
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty())
|
||||
{
|
||||
_logger.Trace("Skipping add for indexer {0} [{1}] due to no app Sync Categories supported by the indexer", indexer.Name, indexer.Id);
|
||||
|
||||
@@ -74,7 +78,7 @@ namespace NzbDrone.Core.Applications.Mylar
|
||||
|
||||
_logger.Trace("Adding indexer {0} [{1}]", indexer.Name, indexer.Id);
|
||||
|
||||
var mylarIndexer = BuildMylarIndexer(indexer, indexer.Protocol);
|
||||
var mylarIndexer = BuildMylarIndexer(indexer, indexerCapabilities, indexer.Protocol);
|
||||
|
||||
var remoteIndexer = _mylarV3Proxy.AddIndexer(mylarIndexer, Settings);
|
||||
|
||||
@@ -107,11 +111,12 @@ namespace NzbDrone.Core.Applications.Mylar
|
||||
{
|
||||
_logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id);
|
||||
|
||||
var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities();
|
||||
var appMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id);
|
||||
var indexerMapping = appMappings.FirstOrDefault(m => m.IndexerId == indexer.Id);
|
||||
var indexerProps = indexerMapping.RemoteIndexerName.Split(",");
|
||||
|
||||
var mylarIndexer = BuildMylarIndexer(indexer, indexer.Protocol, indexerProps[1]);
|
||||
var mylarIndexer = BuildMylarIndexer(indexer, indexerCapabilities, indexer.Protocol, indexerProps[1]);
|
||||
|
||||
//Use the old remote id to find the indexer on Mylar incase the update was from a name change in Prowlarr
|
||||
var remoteIndexer = _mylarV3Proxy.GetIndexer(indexerProps[1], mylarIndexer.Type, Settings);
|
||||
@@ -133,7 +138,7 @@ namespace NzbDrone.Core.Applications.Mylar
|
||||
{
|
||||
_appIndexerMapService.Delete(indexerMapping.Id);
|
||||
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
{
|
||||
_logger.Debug("Remote indexer not found, re-adding {0} [{1}] to Mylar", indexer.Name, indexer.Id);
|
||||
var newRemoteIndexer = _mylarV3Proxy.AddIndexer(mylarIndexer, Settings);
|
||||
@@ -146,7 +151,7 @@ namespace NzbDrone.Core.Applications.Mylar
|
||||
}
|
||||
}
|
||||
|
||||
private MylarIndexer BuildMylarIndexer(IndexerDefinition indexer, DownloadProtocol protocol, string originalName = null)
|
||||
private MylarIndexer BuildMylarIndexer(IndexerDefinition indexer, IndexerCapabilities indexerCapabilities, DownloadProtocol protocol, string originalName = null)
|
||||
{
|
||||
var schema = protocol == DownloadProtocol.Usenet ? MylarProviderType.Newznab : MylarProviderType.Torznab;
|
||||
|
||||
@@ -156,7 +161,7 @@ namespace NzbDrone.Core.Applications.Mylar
|
||||
Altername = $"{indexer.Name} (Prowlarr)",
|
||||
Host = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/api",
|
||||
Apikey = _configFileProvider.ApiKey,
|
||||
Categories = string.Join(",", indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray())),
|
||||
Categories = string.Join(",", indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray())),
|
||||
Enabled = indexer.Enable,
|
||||
Type = schema,
|
||||
};
|
||||
|
||||
@@ -21,13 +21,15 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
private readonly IRadarrV3Proxy _radarrV3Proxy;
|
||||
private readonly ICached<List<RadarrIndexer>> _schemaCache;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly IIndexerFactory _indexerFactory;
|
||||
|
||||
public Radarr(ICacheManager cacheManager, IRadarrV3Proxy radarrV3Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, Logger logger)
|
||||
public Radarr(ICacheManager cacheManager, IRadarrV3Proxy radarrV3Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, IIndexerFactory indexerFactory, Logger logger)
|
||||
: base(appIndexerMapService, logger)
|
||||
{
|
||||
_schemaCache = cacheManager.GetCache<List<RadarrIndexer>>(GetType());
|
||||
_radarrV3Proxy = radarrV3Proxy;
|
||||
_configFileProvider = configFileProvider;
|
||||
_indexerFactory = indexerFactory;
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
@@ -49,7 +51,7 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
try
|
||||
{
|
||||
failures.AddIfNotNull(_radarrV3Proxy.TestConnection(BuildRadarrIndexer(testIndexer, DownloadProtocol.Usenet), Settings));
|
||||
failures.AddIfNotNull(_radarrV3Proxy.TestConnection(BuildRadarrIndexer(testIndexer, testIndexer.Capabilities, DownloadProtocol.Usenet), Settings));
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
@@ -118,7 +120,9 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
public override void AddIndexer(IndexerDefinition indexer)
|
||||
{
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty())
|
||||
var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities();
|
||||
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty())
|
||||
{
|
||||
_logger.Trace("Skipping add for indexer {0} [{1}] due to no app Sync Categories supported by the indexer", indexer.Name, indexer.Id);
|
||||
|
||||
@@ -127,7 +131,7 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
_logger.Trace("Adding indexer {0} [{1}]", indexer.Name, indexer.Id);
|
||||
|
||||
var radarrIndexer = BuildRadarrIndexer(indexer, indexer.Protocol);
|
||||
var radarrIndexer = BuildRadarrIndexer(indexer, indexerCapabilities, indexer.Protocol);
|
||||
|
||||
var remoteIndexer = _radarrV3Proxy.AddIndexer(radarrIndexer, Settings);
|
||||
|
||||
@@ -159,10 +163,11 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
{
|
||||
_logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id);
|
||||
|
||||
var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities();
|
||||
var appMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id);
|
||||
var indexerMapping = appMappings.FirstOrDefault(m => m.IndexerId == indexer.Id);
|
||||
|
||||
var radarrIndexer = BuildRadarrIndexer(indexer, indexer.Protocol, indexerMapping?.RemoteIndexerId ?? 0);
|
||||
var radarrIndexer = BuildRadarrIndexer(indexer, indexerCapabilities, indexer.Protocol, indexerMapping?.RemoteIndexerId ?? 0);
|
||||
|
||||
var remoteIndexer = _radarrV3Proxy.GetIndexer(indexerMapping.RemoteIndexerId, Settings);
|
||||
|
||||
@@ -172,7 +177,7 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
if (!radarrIndexer.Equals(remoteIndexer) || forceSync)
|
||||
{
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
{
|
||||
// Retain user fields not-affiliated with Prowlarr
|
||||
radarrIndexer.Fields.AddRange(remoteIndexer.Fields.Where(f => radarrIndexer.Fields.All(s => s.Name != f.Name)));
|
||||
@@ -198,7 +203,7 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
{
|
||||
_appIndexerMapService.Delete(indexerMapping.Id);
|
||||
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
{
|
||||
_logger.Debug("Remote indexer not found, re-adding {0} [{1}] to Radarr", indexer.Name, indexer.Id);
|
||||
radarrIndexer.Id = 0;
|
||||
@@ -212,7 +217,7 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
}
|
||||
}
|
||||
|
||||
private RadarrIndexer BuildRadarrIndexer(IndexerDefinition indexer, DownloadProtocol protocol, int id = 0)
|
||||
private RadarrIndexer BuildRadarrIndexer(IndexerDefinition indexer, IndexerCapabilities indexerCapabilities, DownloadProtocol protocol, int id = 0)
|
||||
{
|
||||
var cacheKey = $"{Settings.BaseUrl}";
|
||||
var schemas = _schemaCache.Get(cacheKey, () => _radarrV3Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7));
|
||||
@@ -248,7 +253,7 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/";
|
||||
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api";
|
||||
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;
|
||||
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));
|
||||
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));
|
||||
|
||||
if (indexer.Protocol == DownloadProtocol.Torrent)
|
||||
{
|
||||
@@ -258,7 +263,7 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
if (radarrIndexer.Fields.Any(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing"))
|
||||
{
|
||||
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.RejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value = Settings.SyncRejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,9 +37,12 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
[FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Radarr in Settings/General")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
|
||||
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), HelpText = "Only Indexers that support these categories will be synced", Advanced = true)]
|
||||
public IEnumerable<int> SyncCategories { get; set; }
|
||||
|
||||
[FieldDefinition(4, Type = FieldType.Checkbox, Label = "ApplicationSettingsSyncRejectBlocklistedTorrentHashes", HelpText = "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
|
||||
public bool SyncRejectBlocklistedTorrentHashesWhileGrabbing { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
||||
@@ -21,13 +21,15 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
private readonly ICached<List<ReadarrIndexer>> _schemaCache;
|
||||
private readonly IReadarrV1Proxy _readarrV1Proxy;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly IIndexerFactory _indexerFactory;
|
||||
|
||||
public Readarr(ICacheManager cacheManager, IReadarrV1Proxy readarrV1Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, Logger logger)
|
||||
public Readarr(ICacheManager cacheManager, IReadarrV1Proxy readarrV1Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, IIndexerFactory indexerFactory, Logger logger)
|
||||
: base(appIndexerMapService, logger)
|
||||
{
|
||||
_schemaCache = cacheManager.GetCache<List<ReadarrIndexer>>(GetType());
|
||||
_readarrV1Proxy = readarrV1Proxy;
|
||||
_configFileProvider = configFileProvider;
|
||||
_indexerFactory = indexerFactory;
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
@@ -49,7 +51,7 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
|
||||
try
|
||||
{
|
||||
failures.AddIfNotNull(_readarrV1Proxy.TestConnection(BuildReadarrIndexer(testIndexer, DownloadProtocol.Usenet), Settings));
|
||||
failures.AddIfNotNull(_readarrV1Proxy.TestConnection(BuildReadarrIndexer(testIndexer, testIndexer.Capabilities, DownloadProtocol.Usenet), Settings));
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
@@ -118,7 +120,9 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
|
||||
public override void AddIndexer(IndexerDefinition indexer)
|
||||
{
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty())
|
||||
var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities();
|
||||
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty())
|
||||
{
|
||||
_logger.Trace("Skipping add for indexer {0} [{1}] due to no app Sync Categories supported by the indexer", indexer.Name, indexer.Id);
|
||||
|
||||
@@ -127,7 +131,7 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
|
||||
_logger.Trace("Adding indexer {0} [{1}]", indexer.Name, indexer.Id);
|
||||
|
||||
var readarrIndexer = BuildReadarrIndexer(indexer, indexer.Protocol);
|
||||
var readarrIndexer = BuildReadarrIndexer(indexer, indexerCapabilities, indexer.Protocol);
|
||||
|
||||
var remoteIndexer = _readarrV1Proxy.AddIndexer(readarrIndexer, Settings);
|
||||
|
||||
@@ -159,10 +163,11 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
{
|
||||
_logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id);
|
||||
|
||||
var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities();
|
||||
var appMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id);
|
||||
var indexerMapping = appMappings.FirstOrDefault(m => m.IndexerId == indexer.Id);
|
||||
|
||||
var readarrIndexer = BuildReadarrIndexer(indexer, indexer.Protocol, indexerMapping?.RemoteIndexerId ?? 0);
|
||||
var readarrIndexer = BuildReadarrIndexer(indexer, indexerCapabilities, indexer.Protocol, indexerMapping?.RemoteIndexerId ?? 0);
|
||||
|
||||
var remoteIndexer = _readarrV1Proxy.GetIndexer(indexerMapping.RemoteIndexerId, Settings);
|
||||
|
||||
@@ -174,7 +179,7 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
{
|
||||
_logger.Debug("Syncing remote indexer with current settings");
|
||||
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
{
|
||||
// Retain user fields not-affiliated with Prowlarr
|
||||
readarrIndexer.Fields.AddRange(remoteIndexer.Fields.Where(f => readarrIndexer.Fields.All(s => s.Name != f.Name)));
|
||||
@@ -200,7 +205,7 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
{
|
||||
_appIndexerMapService.Delete(indexerMapping.Id);
|
||||
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
{
|
||||
_logger.Debug("Remote indexer not found, re-adding {0} [{1}] to Readarr", indexer.Name, indexer.Id);
|
||||
readarrIndexer.Id = 0;
|
||||
@@ -214,7 +219,7 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
}
|
||||
}
|
||||
|
||||
private ReadarrIndexer BuildReadarrIndexer(IndexerDefinition indexer, DownloadProtocol protocol, int id = 0)
|
||||
private ReadarrIndexer BuildReadarrIndexer(IndexerDefinition indexer, IndexerCapabilities indexerCapabilities, DownloadProtocol protocol, int id = 0)
|
||||
{
|
||||
var cacheKey = $"{Settings.BaseUrl}";
|
||||
var schemas = _schemaCache.Get(cacheKey, () => _readarrV1Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7));
|
||||
@@ -244,7 +249,7 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/";
|
||||
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api";
|
||||
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;
|
||||
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));
|
||||
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));
|
||||
|
||||
if (indexer.Protocol == DownloadProtocol.Torrent)
|
||||
{
|
||||
@@ -259,7 +264,7 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
|
||||
if (readarrIndexer.Fields.Any(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing"))
|
||||
{
|
||||
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.RejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value = Settings.SyncRejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,9 +37,12 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
[FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Readarr in Settings/General")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
|
||||
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), HelpText = "Only Indexers that support these categories will be synced", Advanced = true)]
|
||||
public IEnumerable<int> SyncCategories { get; set; }
|
||||
|
||||
[FieldDefinition(4, Type = FieldType.Checkbox, Label = "ApplicationSettingsSyncRejectBlocklistedTorrentHashes", HelpText = "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
|
||||
public bool SyncRejectBlocklistedTorrentHashesWhileGrabbing { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
||||
@@ -21,13 +21,15 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
private readonly ICached<List<SonarrIndexer>> _schemaCache;
|
||||
private readonly ISonarrV3Proxy _sonarrV3Proxy;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly IIndexerFactory _indexerFactory;
|
||||
|
||||
public Sonarr(ICacheManager cacheManager, ISonarrV3Proxy sonarrV3Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, Logger logger)
|
||||
public Sonarr(ICacheManager cacheManager, ISonarrV3Proxy sonarrV3Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, IIndexerFactory indexerFactory, Logger logger)
|
||||
: base(appIndexerMapService, logger)
|
||||
{
|
||||
_schemaCache = cacheManager.GetCache<List<SonarrIndexer>>(GetType());
|
||||
_sonarrV3Proxy = sonarrV3Proxy;
|
||||
_configFileProvider = configFileProvider;
|
||||
_indexerFactory = indexerFactory;
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
@@ -49,7 +51,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
|
||||
try
|
||||
{
|
||||
failures.AddIfNotNull(_sonarrV3Proxy.TestConnection(BuildSonarrIndexer(testIndexer, DownloadProtocol.Usenet), Settings));
|
||||
failures.AddIfNotNull(_sonarrV3Proxy.TestConnection(BuildSonarrIndexer(testIndexer, testIndexer.Capabilities, DownloadProtocol.Usenet), Settings));
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
@@ -122,8 +124,10 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
|
||||
public override void AddIndexer(IndexerDefinition indexer)
|
||||
{
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty() &&
|
||||
indexer.Capabilities.Categories.SupportedCategories(Settings.AnimeSyncCategories.ToArray()).Empty())
|
||||
var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities();
|
||||
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty() &&
|
||||
indexerCapabilities.Categories.SupportedCategories(Settings.AnimeSyncCategories.ToArray()).Empty())
|
||||
{
|
||||
_logger.Trace("Skipping add for indexer {0} [{1}] due to no app Sync Categories supported by the indexer", indexer.Name, indexer.Id);
|
||||
|
||||
@@ -132,7 +136,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
|
||||
_logger.Trace("Adding indexer {0} [{1}]", indexer.Name, indexer.Id);
|
||||
|
||||
var sonarrIndexer = BuildSonarrIndexer(indexer, indexer.Protocol);
|
||||
var sonarrIndexer = BuildSonarrIndexer(indexer, indexerCapabilities, indexer.Protocol);
|
||||
|
||||
var remoteIndexer = _sonarrV3Proxy.AddIndexer(sonarrIndexer, Settings);
|
||||
|
||||
@@ -164,10 +168,11 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
{
|
||||
_logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id);
|
||||
|
||||
var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities();
|
||||
var appMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id);
|
||||
var indexerMapping = appMappings.FirstOrDefault(m => m.IndexerId == indexer.Id);
|
||||
|
||||
var sonarrIndexer = BuildSonarrIndexer(indexer, indexer.Protocol, indexerMapping?.RemoteIndexerId ?? 0);
|
||||
var sonarrIndexer = BuildSonarrIndexer(indexer, indexerCapabilities, indexer.Protocol, indexerMapping?.RemoteIndexerId ?? 0);
|
||||
|
||||
var remoteIndexer = _sonarrV3Proxy.GetIndexer(indexerMapping.RemoteIndexerId, Settings);
|
||||
|
||||
@@ -179,7 +184,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
{
|
||||
_logger.Debug("Syncing remote indexer with current settings");
|
||||
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any() || indexer.Capabilities.Categories.SupportedCategories(Settings.AnimeSyncCategories.ToArray()).Any())
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any() || indexerCapabilities.Categories.SupportedCategories(Settings.AnimeSyncCategories.ToArray()).Any())
|
||||
{
|
||||
// Retain user fields not-affiliated with Prowlarr
|
||||
sonarrIndexer.Fields.AddRange(remoteIndexer.Fields.Where(f => sonarrIndexer.Fields.All(s => s.Name != f.Name)));
|
||||
@@ -206,7 +211,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
{
|
||||
_appIndexerMapService.Delete(indexerMapping.Id);
|
||||
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any() || indexer.Capabilities.Categories.SupportedCategories(Settings.AnimeSyncCategories.ToArray()).Any())
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any() || indexerCapabilities.Categories.SupportedCategories(Settings.AnimeSyncCategories.ToArray()).Any())
|
||||
{
|
||||
_logger.Debug("Remote indexer not found, re-adding {0} [{1}] to Sonarr", indexer.Name, indexer.Id);
|
||||
sonarrIndexer.Id = 0;
|
||||
@@ -220,7 +225,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
}
|
||||
}
|
||||
|
||||
private SonarrIndexer BuildSonarrIndexer(IndexerDefinition indexer, DownloadProtocol protocol, int id = 0)
|
||||
private SonarrIndexer BuildSonarrIndexer(IndexerDefinition indexer, IndexerCapabilities indexerCapabilities, DownloadProtocol protocol, int id = 0)
|
||||
{
|
||||
var cacheKey = $"{Settings.BaseUrl}";
|
||||
var schemas = _schemaCache.Get(cacheKey, () => _sonarrV3Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7));
|
||||
@@ -256,8 +261,8 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/";
|
||||
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api";
|
||||
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;
|
||||
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));
|
||||
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "animeCategories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.AnimeSyncCategories.ToArray()));
|
||||
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));
|
||||
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "animeCategories").Value = JArray.FromObject(indexerCapabilities.Categories.SupportedCategories(Settings.AnimeSyncCategories.ToArray()));
|
||||
|
||||
if (sonarrIndexer.Fields.Any(x => x.Name == "animeStandardFormatSearch"))
|
||||
{
|
||||
@@ -273,7 +278,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
|
||||
if (sonarrIndexer.Fields.Any(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing"))
|
||||
{
|
||||
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.RejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value = Settings.SyncRejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,9 +43,12 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
[FieldDefinition(4, Label = "Anime Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
|
||||
public IEnumerable<int> AnimeSyncCategories { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Sync Anime Standard Format Search", Type = FieldType.Checkbox, Advanced = true, HelpText = "Sync also searching for anime using the standard numbering")]
|
||||
[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; }
|
||||
|
||||
[FieldDefinition(6, Type = FieldType.Checkbox, Label = "ApplicationSettingsSyncRejectBlocklistedTorrentHashes", HelpText = "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
|
||||
public bool SyncRejectBlocklistedTorrentHashesWhileGrabbing { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
||||
@@ -21,13 +21,15 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
private readonly IWhisparrV3Proxy _whisparrV3Proxy;
|
||||
private readonly ICached<List<WhisparrIndexer>> _schemaCache;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly IIndexerFactory _indexerFactory;
|
||||
|
||||
public Whisparr(ICacheManager cacheManager, IWhisparrV3Proxy whisparrV3Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, Logger logger)
|
||||
public Whisparr(ICacheManager cacheManager, IWhisparrV3Proxy whisparrV3Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, IIndexerFactory indexerFactory, Logger logger)
|
||||
: base(appIndexerMapService, logger)
|
||||
{
|
||||
_schemaCache = cacheManager.GetCache<List<WhisparrIndexer>>(GetType());
|
||||
_whisparrV3Proxy = whisparrV3Proxy;
|
||||
_configFileProvider = configFileProvider;
|
||||
_indexerFactory = indexerFactory;
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
@@ -49,7 +51,7 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
|
||||
try
|
||||
{
|
||||
failures.AddIfNotNull(_whisparrV3Proxy.TestConnection(BuildWhisparrIndexer(testIndexer, DownloadProtocol.Usenet), Settings));
|
||||
failures.AddIfNotNull(_whisparrV3Proxy.TestConnection(BuildWhisparrIndexer(testIndexer, testIndexer.Capabilities, DownloadProtocol.Usenet), Settings));
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
@@ -118,7 +120,9 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
|
||||
public override void AddIndexer(IndexerDefinition indexer)
|
||||
{
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty())
|
||||
var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities();
|
||||
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Empty())
|
||||
{
|
||||
_logger.Trace("Skipping add for indexer {0} [{1}] due to no app Sync Categories supported by the indexer", indexer.Name, indexer.Id);
|
||||
|
||||
@@ -127,7 +131,7 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
|
||||
_logger.Trace("Adding indexer {0} [{1}]", indexer.Name, indexer.Id);
|
||||
|
||||
var whisparrIndexer = BuildWhisparrIndexer(indexer, indexer.Protocol);
|
||||
var whisparrIndexer = BuildWhisparrIndexer(indexer, indexerCapabilities, indexer.Protocol);
|
||||
|
||||
var remoteIndexer = _whisparrV3Proxy.AddIndexer(whisparrIndexer, Settings);
|
||||
|
||||
@@ -159,10 +163,11 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
{
|
||||
_logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id);
|
||||
|
||||
var indexerCapabilities = _indexerFactory.GetInstance(indexer).GetCapabilities();
|
||||
var appMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id);
|
||||
var indexerMapping = appMappings.FirstOrDefault(m => m.IndexerId == indexer.Id);
|
||||
|
||||
var whisparrIndexer = BuildWhisparrIndexer(indexer, indexer.Protocol, indexerMapping?.RemoteIndexerId ?? 0);
|
||||
var whisparrIndexer = BuildWhisparrIndexer(indexer, indexerCapabilities, indexer.Protocol, indexerMapping?.RemoteIndexerId ?? 0);
|
||||
|
||||
var remoteIndexer = _whisparrV3Proxy.GetIndexer(indexerMapping.RemoteIndexerId, Settings);
|
||||
|
||||
@@ -174,7 +179,7 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
{
|
||||
_logger.Debug("Syncing remote indexer with current settings");
|
||||
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
{
|
||||
// Retain user fields not-affiliated with Prowlarr
|
||||
whisparrIndexer.Fields.AddRange(remoteIndexer.Fields.Where(f => whisparrIndexer.Fields.All(s => s.Name != f.Name)));
|
||||
@@ -200,7 +205,7 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
{
|
||||
_appIndexerMapService.Delete(indexerMapping.Id);
|
||||
|
||||
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
if (indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
|
||||
{
|
||||
_logger.Debug("Remote indexer not found, re-adding {0} [{1}] to Whisparr", indexer.Name, indexer.Id);
|
||||
whisparrIndexer.Id = 0;
|
||||
@@ -214,7 +219,7 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
}
|
||||
}
|
||||
|
||||
private WhisparrIndexer BuildWhisparrIndexer(IndexerDefinition indexer, DownloadProtocol protocol, int id = 0)
|
||||
private WhisparrIndexer BuildWhisparrIndexer(IndexerDefinition indexer, IndexerCapabilities indexerCapabilities, DownloadProtocol protocol, int id = 0)
|
||||
{
|
||||
var cacheKey = $"{Settings.BaseUrl}";
|
||||
var schemas = _schemaCache.Get(cacheKey, () => _whisparrV3Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7));
|
||||
@@ -244,7 +249,7 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
whisparrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/";
|
||||
whisparrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api";
|
||||
whisparrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;
|
||||
whisparrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));
|
||||
whisparrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexerCapabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));
|
||||
|
||||
if (indexer.Protocol == DownloadProtocol.Torrent)
|
||||
{
|
||||
@@ -259,7 +264,7 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
|
||||
if (whisparrIndexer.Fields.Any(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing"))
|
||||
{
|
||||
whisparrIndexer.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.RejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
whisparrIndexer.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value = Settings.SyncRejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,9 +37,12 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
[FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Whisparr in Settings/General")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
|
||||
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), HelpText = "Only Indexers that support these categories will be synced", Advanced = true)]
|
||||
public IEnumerable<int> SyncCategories { get; set; }
|
||||
|
||||
[FieldDefinition(4, Type = FieldType.Checkbox, Label = "ApplicationSettingsSyncRejectBlocklistedTorrentHashes", HelpText = "ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
|
||||
public bool SyncRejectBlocklistedTorrentHashesWhileGrabbing { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
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(40)]
|
||||
public class newznab_category_to_capabilities_settings : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Execute.WithConnection(MoveCategoriesToCapabilities);
|
||||
}
|
||||
|
||||
private void MoveCategoriesToCapabilities(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
var updated = new List<object>();
|
||||
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.Transaction = tran;
|
||||
cmd.CommandText = "SELECT \"Id\", \"Settings\" FROM \"Indexers\" WHERE \"Implementation\" IN ('Newznab', 'Torznab')";
|
||||
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
var id = reader.GetInt32(0);
|
||||
var settings = Json.Deserialize<JObject>(reader.GetString(1));
|
||||
|
||||
if ((settings.Value<JObject>("capabilities")?.ContainsKey("categories") ?? false) == false
|
||||
&& settings.ContainsKey("categories")
|
||||
&& settings.TryGetValue("categories", out var categories))
|
||||
{
|
||||
if (!settings.ContainsKey("capabilities"))
|
||||
{
|
||||
settings.Add("capabilities", new JObject());
|
||||
}
|
||||
|
||||
settings.Value<JObject>("capabilities")?.Add(new JProperty("categories", JArray.FromObject(categories)));
|
||||
|
||||
if (settings.ContainsKey("categories"))
|
||||
{
|
||||
settings.Remove("categories");
|
||||
}
|
||||
}
|
||||
|
||||
updated.Add(new
|
||||
{
|
||||
Settings = settings.ToJson(),
|
||||
Id = id
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var updateSql = "UPDATE \"Indexers\" SET \"Settings\" = @Settings WHERE \"Id\" = @Id";
|
||||
conn.Execute(updateSql, updated, transaction: tran);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Download.Clients.Sabnzbd.JsonConverters;
|
||||
@@ -7,10 +7,14 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
{
|
||||
public class SabnzbdConfig
|
||||
{
|
||||
public SabnzbdConfig()
|
||||
{
|
||||
Categories = new List<SabnzbdCategory>();
|
||||
Servers = new List<object>();
|
||||
}
|
||||
|
||||
public SabnzbdConfigMisc Misc { get; set; }
|
||||
|
||||
public List<SabnzbdCategory> Categories { get; set; }
|
||||
|
||||
public List<object> Servers { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using NLog;
|
||||
using System.Collections.Generic;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Configuration.Events;
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
@@ -28,7 +29,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
_logger.Warn("Please update your API key to be at least {0} characters long. You can do this via settings or the config file", MinimumLength);
|
||||
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("ApiKeyValidationHealthCheckMessage"), MinimumLength), "#invalid-api-key");
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, _localizationService.GetLocalizedString("ApiKeyValidationHealthCheckMessage", new Dictionary<string, object> { { "length", MinimumLength } }), "#invalid-api-key");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType());
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Download;
|
||||
@@ -39,10 +40,19 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
|
||||
if (backOffProviders.Count == enabledProviders.Count)
|
||||
{
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Error, _localizationService.GetLocalizedString("DownloadClientStatusCheckAllClientMessage"), "#download-clients-are-unavailable-due-to-failures");
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Error,
|
||||
_localizationService.GetLocalizedString("DownloadClientStatusAllClientHealthCheckMessage"),
|
||||
"#download-clients-are-unavailable-due-to-failures");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("DownloadClientStatusCheckSingleClientMessage"), string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))), "#download-clients-are-unavailable-due-to-failures");
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Warning,
|
||||
_localizationService.GetLocalizedString("DownloadClientStatusSingleClientHealthCheckMessage", new Dictionary<string, object>
|
||||
{
|
||||
{ "downloadClientNames", string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name)) }
|
||||
}),
|
||||
"#download-clients-are-unavailable-due-to-failures");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Indexers;
|
||||
@@ -39,7 +40,10 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Warning,
|
||||
string.Format(_localizationService.GetLocalizedString("IndexerDownloadClientHealthCheckMessage"), string.Join(", ", invalidIndexers.Select(v => v.Name).ToArray())),
|
||||
_localizationService.GetLocalizedString("IndexerDownloadClientHealthCheckMessage", new Dictionary<string, object>
|
||||
{
|
||||
{ "indexerNames", string.Join(", ", invalidIndexers.Select(v => v.Name).ToArray()) }
|
||||
}),
|
||||
"#invalid-indexer-download-client-setting");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Indexers;
|
||||
@@ -17,9 +18,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
private readonly IIndexerFactory _providerFactory;
|
||||
private readonly IIndexerStatusService _providerStatusService;
|
||||
|
||||
public IndexerLongTermStatusCheck(IIndexerFactory providerFactory,
|
||||
IIndexerStatusService providerStatusService,
|
||||
ILocalizationService localizationService)
|
||||
public IndexerLongTermStatusCheck(IIndexerFactory providerFactory, IIndexerStatusService providerStatusService, ILocalizationService localizationService)
|
||||
: base(localizationService)
|
||||
{
|
||||
_providerFactory = providerFactory;
|
||||
@@ -46,14 +45,16 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Error,
|
||||
_localizationService.GetLocalizedString("IndexerLongTermStatusCheckAllClientMessage"),
|
||||
_localizationService.GetLocalizedString("IndexerLongTermStatusAllUnavailableHealthCheckMessage"),
|
||||
"#indexers-are-unavailable-due-to-failures");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Warning,
|
||||
string.Format(_localizationService.GetLocalizedString("IndexerLongTermStatusCheckSingleClientMessage"),
|
||||
string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))),
|
||||
_localizationService.GetLocalizedString("IndexerLongTermStatusUnavailableHealthCheckMessage", new Dictionary<string, object>
|
||||
{
|
||||
{ "indexerNames", string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name)) }
|
||||
}),
|
||||
"#indexers-are-unavailable-due-to-failures");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.IndexerProxies;
|
||||
@@ -9,11 +10,11 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
[CheckOn(typeof(ProviderDeletedEvent<IIndexerProxy>))]
|
||||
[CheckOn(typeof(ProviderAddedEvent<IIndexerProxy>))]
|
||||
[CheckOn(typeof(ProviderUpdatedEvent<IIndexerProxy>))]
|
||||
public class IndexerProxyCheck : HealthCheckBase
|
||||
public class IndexerProxyStatusCheck : HealthCheckBase
|
||||
{
|
||||
private readonly IIndexerProxyFactory _proxyFactory;
|
||||
|
||||
public IndexerProxyCheck(IIndexerProxyFactory proxyFactory,
|
||||
public IndexerProxyStatusCheck(IIndexerProxyFactory proxyFactory,
|
||||
ILocalizationService localizationService)
|
||||
: base(localizationService)
|
||||
{
|
||||
@@ -37,15 +38,17 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Error,
|
||||
_localizationService.GetLocalizedString("IndexerProxyStatusCheckAllClientMessage"),
|
||||
"#proxies-are-unavailable-due-to-failures");
|
||||
_localizationService.GetLocalizedString("IndexerProxyStatusAllUnavailableHealthCheckMessage"),
|
||||
"#indexer-proxies-are-unavailable-due-to-failures");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Warning,
|
||||
string.Format(_localizationService.GetLocalizedString("IndexerProxyStatusCheckSingleClientMessage"),
|
||||
string.Join(", ", badProxies.Select(v => v.Definition.Name))),
|
||||
"#proxies-are-unavailable-due-to-failures");
|
||||
_localizationService.GetLocalizedString("IndexerProxyStatusUnavailableHealthCheckMessage", new Dictionary<string, object>
|
||||
{
|
||||
{ "indexerProxyNames", string.Join(", ", badProxies.Select(v => v.Definition.Name)) }
|
||||
}),
|
||||
"#indexer-proxies-are-unavailable-due-to-failures");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Indexers;
|
||||
@@ -44,14 +45,16 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Error,
|
||||
_localizationService.GetLocalizedString("IndexerStatusCheckAllClientMessage"),
|
||||
_localizationService.GetLocalizedString("IndexerStatusAllUnavailableHealthCheckMessage"),
|
||||
"#indexers-are-unavailable-due-to-failures");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Warning,
|
||||
string.Format(_localizationService.GetLocalizedString("IndexerStatusCheckSingleClientMessage"),
|
||||
string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))),
|
||||
_localizationService.GetLocalizedString("IndexerStatusUnavailableHealthCheckMessage", new Dictionary<string, object>
|
||||
{
|
||||
{ "indexerNames", string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name)) }
|
||||
}),
|
||||
"#indexers-are-unavailable-due-to-failures");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
[CheckOn(typeof(ProviderAddedEvent<IIndexer>))]
|
||||
[CheckOn(typeof(ProviderUpdatedEvent<IIndexer>))]
|
||||
[CheckOn(typeof(ProviderDeletedEvent<IIndexer>))]
|
||||
[CheckOn(typeof(ProviderBulkUpdatedEvent<IIndexer>))]
|
||||
[CheckOn(typeof(ProviderBulkDeletedEvent<IIndexer>))]
|
||||
public class IndexerVIPCheck : HealthCheckBase
|
||||
{
|
||||
@@ -24,7 +25,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
|
||||
public override HealthCheck Check()
|
||||
{
|
||||
var indexers = _indexerFactory.AllProviders(false);
|
||||
var indexers = _indexerFactory.Enabled(false);
|
||||
var expiringProviders = new List<IIndexer>();
|
||||
|
||||
foreach (var provider in indexers)
|
||||
@@ -39,12 +40,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
|
||||
var expiration = (string)vipProp.GetValue(provider.Definition.Settings);
|
||||
|
||||
if (expiration.IsNullOrWhiteSpace())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DateTime.Parse(expiration).Between(DateTime.Now, DateTime.Now.AddDays(7)))
|
||||
if (expiration.IsNotNullOrWhiteSpace() &&
|
||||
DateTime.Parse(expiration).Between(DateTime.Now, DateTime.Now.AddDays(7)))
|
||||
{
|
||||
expiringProviders.Add(provider);
|
||||
}
|
||||
@@ -53,10 +50,12 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
if (!expiringProviders.Empty())
|
||||
{
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Warning,
|
||||
string.Format(_localizationService.GetLocalizedString("IndexerVipCheckExpiringClientMessage"),
|
||||
string.Join(", ", expiringProviders.Select(v => v.Definition.Name))),
|
||||
"#indexer-vip-expiring");
|
||||
HealthCheckResult.Warning,
|
||||
_localizationService.GetLocalizedString("IndexerVipExpiringHealthCheckMessage", new Dictionary<string, object>
|
||||
{
|
||||
{ "indexerNames", string.Join(", ", expiringProviders.Select(v => v.Definition.Name).ToArray()) }
|
||||
}),
|
||||
"#indexer-vip-expiring");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType());
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
[CheckOn(typeof(ProviderAddedEvent<IIndexer>))]
|
||||
[CheckOn(typeof(ProviderUpdatedEvent<IIndexer>))]
|
||||
[CheckOn(typeof(ProviderDeletedEvent<IIndexer>))]
|
||||
[CheckOn(typeof(ProviderBulkUpdatedEvent<IIndexer>))]
|
||||
[CheckOn(typeof(ProviderBulkDeletedEvent<IIndexer>))]
|
||||
public class IndexerVIPExpiredCheck : HealthCheckBase
|
||||
{
|
||||
@@ -24,7 +25,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
|
||||
public override HealthCheck Check()
|
||||
{
|
||||
var indexers = _indexerFactory.AllProviders(false);
|
||||
var indexers = _indexerFactory.Enabled(false);
|
||||
var expiredProviders = new List<IIndexer>();
|
||||
|
||||
foreach (var provider in indexers)
|
||||
@@ -39,12 +40,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
|
||||
var expiration = (string)vipProp.GetValue(provider.Definition.Settings);
|
||||
|
||||
if (expiration.IsNullOrWhiteSpace())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DateTime.Parse(expiration).Before(DateTime.Now))
|
||||
if (expiration.IsNotNullOrWhiteSpace() &&
|
||||
DateTime.Parse(expiration).Before(DateTime.Now))
|
||||
{
|
||||
expiredProviders.Add(provider);
|
||||
}
|
||||
@@ -53,10 +50,12 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
if (!expiredProviders.Empty())
|
||||
{
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Error,
|
||||
string.Format(_localizationService.GetLocalizedString("IndexerVipCheckExpiredClientMessage"),
|
||||
string.Join(", ", expiredProviders.Select(v => v.Definition.Name))),
|
||||
"#indexer-vip-expired");
|
||||
HealthCheckResult.Error,
|
||||
_localizationService.GetLocalizedString("IndexerVipExpiredHealthCheckMessage", new Dictionary<string, object>
|
||||
{
|
||||
{ "indexerNames", string.Join(", ", expiredProviders.Select(v => v.Definition.Name).ToArray()) }
|
||||
}),
|
||||
"#indexer-vip-expired");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType());
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Localization;
|
||||
@@ -45,7 +46,10 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Warning,
|
||||
string.Format(_localizationService.GetLocalizedString("NotificationStatusSingleClientHealthCheckMessage"), string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))),
|
||||
_localizationService.GetLocalizedString("NotificationStatusSingleClientHealthCheckMessage", new Dictionary<string, object>
|
||||
{
|
||||
{ "notificationNames", string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name)) }
|
||||
}),
|
||||
"#notifications-are-unavailable-due-to-failures");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using NLog;
|
||||
@@ -19,7 +20,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
|
||||
private readonly IHttpRequestBuilderFactory _cloudRequestBuilder;
|
||||
|
||||
public ProxyCheck(IProwlarrCloudRequestBuilder cloudRequestBuilder, IConfigService configService, IHttpClient client, ILocalizationService localizationService, Logger logger)
|
||||
public ProxyCheck(IProwlarrCloudRequestBuilder cloudRequestBuilder, IConfigService configService, IHttpClient client, Logger logger, ILocalizationService localizationService)
|
||||
: base(localizationService)
|
||||
{
|
||||
_configService = configService;
|
||||
@@ -31,35 +32,58 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
|
||||
public override HealthCheck Check()
|
||||
{
|
||||
if (_configService.ProxyEnabled)
|
||||
if (!_configService.ProxyEnabled)
|
||||
{
|
||||
var addresses = Dns.GetHostAddresses(_configService.ProxyHostname);
|
||||
if (!addresses.Any())
|
||||
{
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("ProxyCheckResolveIpMessage"), _configService.ProxyHostname));
|
||||
}
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
|
||||
var request = _cloudRequestBuilder.Create()
|
||||
.Resource("/ping")
|
||||
.Build();
|
||||
var addresses = Dns.GetHostAddresses(_configService.ProxyHostname);
|
||||
|
||||
try
|
||||
{
|
||||
var response = _client.Execute(request);
|
||||
|
||||
// We only care about 400 responses, other error codes can be ignored
|
||||
if (response.StatusCode == HttpStatusCode.BadRequest)
|
||||
if (!addresses.Any())
|
||||
{
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Error,
|
||||
_localizationService.GetLocalizedString("ProxyResolveIpHealthCheckMessage", new Dictionary<string, object>
|
||||
{
|
||||
_logger.Error("Proxy Health Check failed: {0}", response.StatusCode);
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("ProxyCheckBadRequestMessage"), response.StatusCode));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{ "proxyHostName", _configService.ProxyHostname }
|
||||
}),
|
||||
"#proxy-failed-resolve-ip");
|
||||
}
|
||||
|
||||
var request = _cloudRequestBuilder.Create()
|
||||
.Resource("/ping")
|
||||
.Build();
|
||||
|
||||
try
|
||||
{
|
||||
var response = _client.Execute(request);
|
||||
|
||||
// We only care about 400 responses, other error codes can be ignored
|
||||
if (response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
_logger.Error(ex, "Proxy Health Check failed");
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("ProxyCheckFailedToTestMessage"), request.Url));
|
||||
_logger.Error("Proxy Health Check failed: {0}", response.StatusCode);
|
||||
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Error,
|
||||
_localizationService.GetLocalizedString("ProxyBadRequestHealthCheckMessage", new Dictionary<string, object>
|
||||
{
|
||||
{ "statusCode", response.StatusCode }
|
||||
}),
|
||||
"#proxy-failed-test");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Proxy Health Check failed");
|
||||
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Error,
|
||||
_localizationService.GetLocalizedString("ProxyFailedToTestHealthCheckMessage", new Dictionary<string, object>
|
||||
{
|
||||
{ "url", request.Url }
|
||||
}),
|
||||
"#proxy-failed-test");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
@@ -47,7 +48,12 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Error,
|
||||
string.Format(_localizationService.GetLocalizedString("UpdateCheckStartupTranslocationMessage"), startupFolder),
|
||||
_localizationService.GetLocalizedString(
|
||||
"UpdateStartupTranslocationHealthCheckMessage",
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
{ "startupFolder", startupFolder }
|
||||
}),
|
||||
"#cannot-install-update-because-startup-folder-is-in-an-app-translocation-folder.");
|
||||
}
|
||||
|
||||
@@ -55,7 +61,13 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Error,
|
||||
string.Format(_localizationService.GetLocalizedString("UpdateCheckStartupNotWritableMessage"), startupFolder, Environment.UserName),
|
||||
_localizationService.GetLocalizedString(
|
||||
"UpdateStartupNotWritableHealthCheckMessage",
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
{ "startupFolder", startupFolder },
|
||||
{ "userName", Environment.UserName }
|
||||
}),
|
||||
"#cannot-install-update-because-startup-folder-is-not-writable-by-the-user");
|
||||
}
|
||||
|
||||
@@ -63,14 +75,20 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Error,
|
||||
string.Format(_localizationService.GetLocalizedString("UpdateCheckUINotWritableMessage"), uiFolder, Environment.UserName),
|
||||
_localizationService.GetLocalizedString(
|
||||
"UpdateUiNotWritableHealthCheckMessage",
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
{ "uiFolder", uiFolder },
|
||||
{ "userName", Environment.UserName }
|
||||
}),
|
||||
"#cannot-install-update-because-ui-folder-is-not-writable-by-the-user");
|
||||
}
|
||||
}
|
||||
|
||||
if (BuildInfo.BuildDateTime < DateTime.UtcNow.AddDays(-14) && _checkUpdateService.AvailableUpdate() != null)
|
||||
{
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, _localizationService.GetLocalizedString("UpdateAvailable"), "#new-update-is-available");
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, _localizationService.GetLocalizedString("UpdateAvailableHealthCheckMessage"));
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType());
|
||||
|
||||
@@ -102,9 +102,15 @@ namespace NzbDrone.Core.IndexerProxies.FlareSolverr
|
||||
var url = request.Url.ToString();
|
||||
var maxTimeout = Settings.RequestTimeout * 1000;
|
||||
|
||||
// Use Proxy if no credentials are set (creds not supported as of FS 2.2.9)
|
||||
var proxySettings = _proxySettingsProvider.GetProxySettings();
|
||||
var proxyUrl = proxySettings != null && proxySettings.Username.IsNullOrWhiteSpace() && proxySettings.Password.IsNullOrWhiteSpace() ? GetProxyUri(proxySettings) : null;
|
||||
var proxyUrl = proxySettings != null ? GetProxyUri(proxySettings) : null;
|
||||
|
||||
var requestProxy = new FlareSolverrProxy
|
||||
{
|
||||
Url = proxyUrl?.OriginalString,
|
||||
Username = proxySettings != null && proxySettings.Username.IsNotNullOrWhiteSpace() ? proxySettings.Username : null,
|
||||
Password = proxySettings != null && proxySettings.Password.IsNotNullOrWhiteSpace() ? proxySettings.Password : null
|
||||
};
|
||||
|
||||
if (request.Method == HttpMethod.Get)
|
||||
{
|
||||
@@ -113,10 +119,7 @@ namespace NzbDrone.Core.IndexerProxies.FlareSolverr
|
||||
Cmd = "request.get",
|
||||
Url = url,
|
||||
MaxTimeout = maxTimeout,
|
||||
Proxy = new FlareSolverrProxy
|
||||
{
|
||||
Url = proxyUrl?.OriginalString
|
||||
}
|
||||
Proxy = requestProxy
|
||||
};
|
||||
}
|
||||
else if (request.Method == HttpMethod.Post)
|
||||
@@ -139,10 +142,7 @@ namespace NzbDrone.Core.IndexerProxies.FlareSolverr
|
||||
ContentLength = null
|
||||
},
|
||||
MaxTimeout = maxTimeout,
|
||||
Proxy = new FlareSolverrProxy
|
||||
{
|
||||
Url = proxyUrl?.OriginalString
|
||||
}
|
||||
Proxy = requestProxy
|
||||
};
|
||||
}
|
||||
else if (contentTypeType.Contains("multipart/form-data")
|
||||
@@ -169,6 +169,7 @@ namespace NzbDrone.Core.IndexerProxies.FlareSolverr
|
||||
newRequest.LogResponseContent = true;
|
||||
newRequest.RequestTimeout = TimeSpan.FromSeconds(Settings.RequestTimeout + 5);
|
||||
newRequest.SetContent(req.ToJson());
|
||||
newRequest.ContentSummary = req.ToJson(Formatting.None);
|
||||
|
||||
_logger.Debug("Cloudflare Detected, Applying FlareSolverr Proxy {0} to request {1}", Name, request.Url);
|
||||
|
||||
@@ -243,6 +244,8 @@ namespace NzbDrone.Core.IndexerProxies.FlareSolverr
|
||||
private class FlareSolverrProxy
|
||||
{
|
||||
public string Url { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
|
||||
private class HeadersPost
|
||||
|
||||
@@ -177,8 +177,8 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
|
||||
if (criteriaBase.Categories is { Length: > 0 })
|
||||
{
|
||||
//Only query supported indexers
|
||||
indexers = indexers.Where(i => ((IndexerDefinition)i.Definition).Capabilities.Categories.SupportedCategories(criteriaBase.Categories).Any()).ToList();
|
||||
// Only query supported indexers
|
||||
indexers = indexers.Where(i => i.GetCapabilities().Categories.SupportedCategories(criteriaBase.Categories).Any()).ToList();
|
||||
|
||||
if (indexers.Count == 0)
|
||||
{
|
||||
@@ -196,7 +196,7 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
|
||||
var reports = batch.SelectMany(x => x).ToList();
|
||||
|
||||
_logger.Debug("Total of {0} reports were found for {1} from {2} indexer(s)", reports.Count, criteriaBase, indexers.Count);
|
||||
_logger.ProgressDebug("Total of {0} reports were found for {1} from {2} indexer(s)", reports.Count, criteriaBase, indexers.Count);
|
||||
|
||||
return reports;
|
||||
}
|
||||
@@ -217,7 +217,7 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
//Filter results to only those in searched categories
|
||||
if (criteriaBase.Categories.Length > 0)
|
||||
{
|
||||
var expandedQueryCats = ((IndexerDefinition)indexer.Definition).Capabilities.Categories.ExpandTorznabQueryCategories(criteriaBase.Categories);
|
||||
var expandedQueryCats = indexer.GetCapabilities().Categories.ExpandTorznabQueryCategories(criteriaBase.Categories);
|
||||
|
||||
releases = releases.Where(result => result.Categories?.Any() != true || expandedQueryCats.Intersect(result.Categories.Select(c => c.Id)).Any()).ToList();
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@ namespace NzbDrone.Core.IndexerVersions
|
||||
"desitorrents",
|
||||
"hdbits",
|
||||
"lat-team",
|
||||
"mteamtp",
|
||||
"mteamtp2fa",
|
||||
"reelflix",
|
||||
"shareisland",
|
||||
"skipthecommercials",
|
||||
|
||||
@@ -467,6 +467,11 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
// Ignore these categories as they'll cause hell with the matcher
|
||||
// TV Special, DVD Special, BD Special
|
||||
if (groupName is "TV Special" or "DVD Special" or "BD Special")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (groupName is "TV Series" or "OVA" or "ONA")
|
||||
{
|
||||
categories = new List<IndexerCategory> { NewznabStandardCategory.TVAnime };
|
||||
@@ -695,7 +700,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
ExcludeHentai = false;
|
||||
SearchByYear = false;
|
||||
EnableSonarrCompatibility = true;
|
||||
UseFilenameForSingleEpisodes = false;
|
||||
UseFilenameForSingleEpisodes = true;
|
||||
AddJapaneseTitle = true;
|
||||
AddRomajiTitle = true;
|
||||
AddAlternativeTitle = true;
|
||||
|
||||
@@ -84,6 +84,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public class AvistaZParser : AvistazParserBase
|
||||
{
|
||||
protected override string TimezoneOffset => "+01:00";
|
||||
protected override string TimezoneOffset => "+02:00";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
{
|
||||
public class AvistazParserBase : IParseIndexerResponse
|
||||
{
|
||||
protected virtual string TimezoneOffset => "-05:00"; // Avistaz does not specify a timezone & returns server time
|
||||
protected virtual string TimezoneOffset => "-04:00"; // Avistaz does not specify a timezone & returns server time
|
||||
private readonly HashSet<string> _hdResolutions = new () { "1080p", "1080i", "720p" };
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
|
||||
@@ -771,13 +771,14 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
|
||||
protected Dictionary<string, string> ParseCustomHeaders(Dictionary<string, List<string>> customHeaders, Dictionary<string, object> variables)
|
||||
{
|
||||
var headers = new Dictionary<string, string>();
|
||||
|
||||
if (customHeaders == null)
|
||||
{
|
||||
return null;
|
||||
return headers;
|
||||
}
|
||||
|
||||
// FIXME: fix jackett header handling (allow it to specifiy the same header multipe times)
|
||||
var headers = new Dictionary<string, string>();
|
||||
foreach (var header in customHeaders)
|
||||
{
|
||||
headers.Add(header.Key, ApplyGoTemplateText(header.Value[0], variables));
|
||||
|
||||
@@ -502,36 +502,54 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
value = release.Description;
|
||||
break;
|
||||
case "category":
|
||||
if (fieldModifiers.Contains("noappend"))
|
||||
{
|
||||
_logger.Warn("The \"noappend\" modifier is deprecated. Please switch to \"default\". See the Definition Format in the Wiki for more information.");
|
||||
}
|
||||
|
||||
var cats = _categories.MapTrackerCatToNewznab(value);
|
||||
|
||||
if (cats.Any())
|
||||
{
|
||||
if (release.Categories == null || fieldModifiers.Contains("noappend"))
|
||||
{
|
||||
release.Categories = cats;
|
||||
}
|
||||
else
|
||||
{
|
||||
release.Categories = release.Categories.Union(cats).ToList();
|
||||
}
|
||||
release.Categories = release.Categories == null || fieldModifiers.Contains("noappend")
|
||||
? cats
|
||||
: release.Categories.Union(cats).ToList();
|
||||
}
|
||||
|
||||
if (value.IsNotNullOrWhiteSpace() && !release.Categories.Any())
|
||||
{
|
||||
_logger.Warn("[{0}] Invalid category for value: '{1}'", _definition.Id, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = release.Categories.ToString();
|
||||
}
|
||||
|
||||
value = release.Categories.ToString();
|
||||
break;
|
||||
case "categorydesc":
|
||||
var catsDesc = _categories.MapTrackerCatDescToNewznab(value);
|
||||
if (catsDesc.Any())
|
||||
if (fieldModifiers.Contains("noappend"))
|
||||
{
|
||||
if (release.Categories == null || fieldModifiers.Contains("noappend"))
|
||||
{
|
||||
release.Categories = catsDesc;
|
||||
}
|
||||
else
|
||||
{
|
||||
release.Categories = release.Categories.Union(catsDesc).ToList();
|
||||
}
|
||||
_logger.Warn("The \"noappend\" modifier is deprecated. Please switch to \"default\". See the Definition Format in the Wiki for more information.");
|
||||
}
|
||||
|
||||
var catsDesc = _categories.MapTrackerCatDescToNewznab(value);
|
||||
|
||||
if (catsDesc.Any())
|
||||
{
|
||||
release.Categories = release.Categories == null || fieldModifiers.Contains("noappend")
|
||||
? catsDesc
|
||||
: release.Categories.Union(catsDesc).ToList();
|
||||
}
|
||||
|
||||
if (value.IsNotNullOrWhiteSpace() && !release.Categories.Any())
|
||||
{
|
||||
_logger.Warn("[{0}] Invalid category for value: '{1}'", _definition.Id, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = release.Categories.ToString();
|
||||
}
|
||||
|
||||
value = release.Categories.ToString();
|
||||
break;
|
||||
case "size":
|
||||
release.Size = ParseUtil.GetBytes(value);
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
|
||||
protected override string TimezoneOffset => "+01:00";
|
||||
protected override string TimezoneOffset => "+02:00";
|
||||
|
||||
public ExoticaZParser(IndexerCapabilitiesCategories categories)
|
||||
{
|
||||
|
||||
@@ -77,7 +77,7 @@ public class FileListParser : IParseIndexerResponse
|
||||
InfoUrl = GetInfoUrl(id),
|
||||
Seeders = row.Seeders,
|
||||
Peers = row.Leechers + row.Seeders,
|
||||
PublishDate = DateTime.Parse(row.UploadDate + " +0200", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal),
|
||||
PublishDate = DateTime.Parse(row.UploadDate + " +0300", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal),
|
||||
Description = row.SmallDescription,
|
||||
Genres = row.SmallDescription.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToList(),
|
||||
ImdbId = imdbId,
|
||||
|
||||
@@ -59,6 +59,12 @@ public class GazelleParser : IParseIndexerResponse
|
||||
{
|
||||
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)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var id = torrent.TorrentId;
|
||||
|
||||
var title = $"{result.Artist} - {result.GroupName} ({result.GroupYear}) [{torrent.Format} {torrent.Encoding}] [{torrent.Media}]";
|
||||
@@ -72,14 +78,14 @@ public class GazelleParser : IParseIndexerResponse
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Guid = infoUrl,
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = GetDownloadUrl(id, !torrent.IsFreeLeech && !torrent.IsNeutralLeech && !torrent.IsPersonalFreeLeech),
|
||||
Title = WebUtility.HtmlDecode(title),
|
||||
Container = torrent.Encoding,
|
||||
Files = torrent.FileCount,
|
||||
Grabs = torrent.Snatches,
|
||||
Codec = torrent.Format,
|
||||
Size = long.Parse(torrent.Size),
|
||||
DownloadUrl = GetDownloadUrl(id, torrent.CanUseToken),
|
||||
InfoUrl = infoUrl,
|
||||
Seeders = int.Parse(torrent.Seeders),
|
||||
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
|
||||
PublishDate = torrent.Time.ToUniversalTime(),
|
||||
@@ -104,6 +110,12 @@ public class GazelleParser : IParseIndexerResponse
|
||||
}
|
||||
else
|
||||
{
|
||||
// skip releases that cannot be used with freeleech tokens when the option is enabled
|
||||
if (Settings.UseFreeleechToken && !result.CanUseToken)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var id = result.TorrentId;
|
||||
var groupName = WebUtility.HtmlDecode(result.GroupName);
|
||||
var infoUrl = GetInfoUrl(result.GroupId, id);
|
||||
@@ -111,10 +123,10 @@ public class GazelleParser : IParseIndexerResponse
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Guid = infoUrl,
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = GetDownloadUrl(id, !result.IsFreeLeech && !result.IsNeutralLeech && !result.IsPersonalFreeLeech),
|
||||
Title = groupName,
|
||||
Size = long.Parse(result.Size),
|
||||
DownloadUrl = GetDownloadUrl(id, result.CanUseToken),
|
||||
InfoUrl = infoUrl,
|
||||
Seeders = int.Parse(result.Seeders),
|
||||
Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders),
|
||||
Files = result.FileCount,
|
||||
|
||||
@@ -168,16 +168,22 @@ public class GreatPosterWallParser : GazelleParser
|
||||
{
|
||||
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)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var infoUrl = GetInfoUrl(result.GroupId.ToString(), torrent.TorrentId);
|
||||
var time = DateTime.SpecifyKind(torrent.Time, DateTimeKind.Unspecified);
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Title = WebUtility.HtmlDecode(torrent.FileName).Trim(),
|
||||
Guid = infoUrl,
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = GetDownloadUrl(torrent.TorrentId, !torrent.IsFreeleech && !torrent.IsNeutralLeech && !torrent.IsPersonalFreeleech),
|
||||
Title = WebUtility.HtmlDecode(torrent.FileName).Trim(),
|
||||
PosterUrl = GetPosterUrl(result.Cover),
|
||||
DownloadUrl = GetDownloadUrl(torrent.TorrentId, torrent.CanUseToken),
|
||||
PublishDate = new DateTimeOffset(time, TimeSpan.FromHours(8)).UtcDateTime, // Time is Chinese Time, add 8 hours difference from UTC
|
||||
Categories = ParseCategories(torrent),
|
||||
Size = torrent.Size,
|
||||
|
||||
@@ -333,7 +333,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var publishDate = DateTimeUtil.FromTimeAgo(dateSplit.First());
|
||||
var description = descrSplit.Length > 1 ? "Tags: " + descrSplit.First().Trim() : "";
|
||||
|
||||
var catIcon = row.QuerySelector("td:nth-of-type(1) a");
|
||||
var catIcon = row.QuerySelector("td:nth-of-type(1) a[href^=\"?\"]");
|
||||
if (catIcon == null)
|
||||
{
|
||||
// Torrents - Category column == Text or Code
|
||||
@@ -342,7 +342,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
}
|
||||
|
||||
// Torrents - Category column == Icons
|
||||
var cat = _categories.MapTrackerCatToNewznab(catIcon.GetAttribute("href").Substring(1));
|
||||
var cat = _categories.MapTrackerCatToNewznab(catIcon.GetAttribute("href")?.Substring(1));
|
||||
|
||||
var size = ParseUtil.GetBytes(row.Children[sizeIndex].TextContent);
|
||||
|
||||
|
||||
496
src/NzbDrone.Core/Indexers/Definitions/MTeamTp.cs
Normal file
496
src/NzbDrone.Core/Indexers/Definitions/MTeamTp.cs
Normal file
@@ -0,0 +1,496 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using FluentValidation;
|
||||
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.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.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions;
|
||||
|
||||
public class MTeamTp : TorrentIndexerBase<MTeamTpSettings>
|
||||
{
|
||||
public override string Name => "M-Team - TP";
|
||||
public override string[] IndexerUrls => new[]
|
||||
{
|
||||
"https://kp.m-team.cc/",
|
||||
"https://tp.m-team.cc/",
|
||||
"https://pt.m-team.cc/"
|
||||
};
|
||||
public override string Description => "M-Team TP (MTTP) is a CHINESE Private Torrent Tracker for HD MOVIES / TV / 3X";
|
||||
public override string Language => "zh-CN";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override int PageSize => 100;
|
||||
public override TimeSpan RateLimit => TimeSpan.FromSeconds(5);
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
public const string UserAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.35";
|
||||
|
||||
public MTeamTp(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new MTeamTpRequestGenerator(Settings, Capabilities);
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new MTeamTpParser(Settings, Capabilities.Categories);
|
||||
}
|
||||
|
||||
public override async Task<byte[]> Download(Uri link)
|
||||
{
|
||||
var request = new HttpRequestBuilder(link.ToString())
|
||||
.SetHeader("x-api-key", Settings.ApiKey)
|
||||
.Accept(HttpAccept.Json)
|
||||
.Post()
|
||||
.Build();
|
||||
|
||||
request.Headers.UserAgent = UserAgent;
|
||||
|
||||
var response = await _httpClient.ExecuteProxiedAsync(request, Definition);
|
||||
|
||||
if (!STJson.TryDeserialize<MTeamTpApiDownloadTokenResponse>(response.Content, out var jsonResponse))
|
||||
{
|
||||
throw new ReleaseDownloadException("Invalid response received from M-Team, not a valid JSON");
|
||||
}
|
||||
|
||||
if (jsonResponse.Data.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new ReleaseDownloadException($"Unable to find download link for: {link}");
|
||||
}
|
||||
|
||||
return await base.Download(new Uri(jsonResponse.Data));
|
||||
}
|
||||
|
||||
protected override Task<HttpRequest> GetDownloadRequest(Uri link)
|
||||
{
|
||||
var request = new HttpRequest(link.AbsoluteUri)
|
||||
{
|
||||
AllowAutoRedirect = true,
|
||||
Headers =
|
||||
{
|
||||
UserAgent = UserAgent
|
||||
}
|
||||
};
|
||||
|
||||
return Task.FromResult(request);
|
||||
}
|
||||
|
||||
protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private static IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(401, NewznabStandardCategory.MoviesSD, "Movie(電影)/SD");
|
||||
caps.Categories.AddCategoryMapping(419, NewznabStandardCategory.MoviesHD, "Movie(電影)/HD");
|
||||
caps.Categories.AddCategoryMapping(420, NewznabStandardCategory.MoviesDVD, "Movie(電影)/DVDiSo");
|
||||
caps.Categories.AddCategoryMapping(421, NewznabStandardCategory.MoviesBluRay, "Movie(電影)/Blu-Ray");
|
||||
caps.Categories.AddCategoryMapping(439, NewznabStandardCategory.MoviesHD, "Movie(電影)/Remux");
|
||||
caps.Categories.AddCategoryMapping(403, NewznabStandardCategory.TVSD, "TV Series(影劇/綜藝)/SD");
|
||||
caps.Categories.AddCategoryMapping(402, NewznabStandardCategory.TVHD, "TV Series(影劇/綜藝)/HD");
|
||||
caps.Categories.AddCategoryMapping(435, NewznabStandardCategory.TVSD, "TV Series(影劇/綜藝)/DVDiSo");
|
||||
caps.Categories.AddCategoryMapping(438, NewznabStandardCategory.TVHD, "TV Series(影劇/綜藝)/BD");
|
||||
caps.Categories.AddCategoryMapping(404, NewznabStandardCategory.TVDocumentary, "紀錄教育");
|
||||
caps.Categories.AddCategoryMapping(405, NewznabStandardCategory.TVAnime, "Anime(動畫)");
|
||||
caps.Categories.AddCategoryMapping(407, NewznabStandardCategory.TVSport, "Sports(運動)");
|
||||
caps.Categories.AddCategoryMapping(422, NewznabStandardCategory.PC0day, "Software(軟體)");
|
||||
caps.Categories.AddCategoryMapping(423, NewznabStandardCategory.PCGames, "PCGame(PC遊戲)");
|
||||
caps.Categories.AddCategoryMapping(427, NewznabStandardCategory.BooksEBook, "Study/Edu ebook(教育書面)");
|
||||
caps.Categories.AddCategoryMapping(441, NewznabStandardCategory.BooksOther, "Study/Edu video(教育影片)");
|
||||
caps.Categories.AddCategoryMapping(442, NewznabStandardCategory.AudioAudiobook, "Study/Edu audio(教育音檔)");
|
||||
caps.Categories.AddCategoryMapping(409, NewznabStandardCategory.Other, "Misc(其他)");
|
||||
|
||||
// music
|
||||
caps.Categories.AddCategoryMapping(406, NewznabStandardCategory.AudioVideo, "MV(演唱)");
|
||||
caps.Categories.AddCategoryMapping(408, NewznabStandardCategory.AudioOther, "Music(AAC/ALAC)");
|
||||
caps.Categories.AddCategoryMapping(434, NewznabStandardCategory.Audio, "Music(無損)");
|
||||
|
||||
// adult
|
||||
caps.Categories.AddCategoryMapping(410, NewznabStandardCategory.XXX, "AV(有碼)/HD Censored");
|
||||
caps.Categories.AddCategoryMapping(429, NewznabStandardCategory.XXX, "AV(無碼)/HD Uncensored");
|
||||
caps.Categories.AddCategoryMapping(424, NewznabStandardCategory.XXXSD, "AV(有碼)/SD Censored");
|
||||
caps.Categories.AddCategoryMapping(430, NewznabStandardCategory.XXXSD, "AV(無碼)/SD Uncensored");
|
||||
caps.Categories.AddCategoryMapping(426, NewznabStandardCategory.XXXDVD, "AV(無碼)/DVDiSo Uncensored");
|
||||
caps.Categories.AddCategoryMapping(437, NewznabStandardCategory.XXXDVD, "AV(有碼)/DVDiSo Censored");
|
||||
caps.Categories.AddCategoryMapping(431, NewznabStandardCategory.XXX, "AV(有碼)/Blu-Ray Censored");
|
||||
caps.Categories.AddCategoryMapping(432, NewznabStandardCategory.XXX, "AV(無碼)/Blu-Ray Uncensored");
|
||||
caps.Categories.AddCategoryMapping(436, NewznabStandardCategory.XXX, "AV(網站)/0Day");
|
||||
caps.Categories.AddCategoryMapping(425, NewznabStandardCategory.XXX, "IV(寫真影集)/Video Collection");
|
||||
caps.Categories.AddCategoryMapping(433, NewznabStandardCategory.XXXImageSet, "IV(寫真圖集)/Picture Collection");
|
||||
caps.Categories.AddCategoryMapping(411, NewznabStandardCategory.XXX, "H-Game(遊戲)");
|
||||
caps.Categories.AddCategoryMapping(412, NewznabStandardCategory.XXX, "H-Anime(動畫)");
|
||||
caps.Categories.AddCategoryMapping(413, NewznabStandardCategory.XXX, "H-Comic(漫畫)");
|
||||
caps.Categories.AddCategoryMapping(440, NewznabStandardCategory.XXX, "AV(Gay)/HD");
|
||||
|
||||
return caps;
|
||||
}
|
||||
}
|
||||
|
||||
public class MTeamTpRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
private readonly MTeamTpSettings _settings;
|
||||
private readonly IndexerCapabilities _capabilities;
|
||||
|
||||
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)
|
||||
{
|
||||
_settings = settings;
|
||||
_capabilities = capabilities;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(MTeamTpRequestType.Normal, searchCriteria, searchCriteria.SanitizedSearchTerm, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(MTeamTpRequestType.Normal, searchCriteria, searchCriteria.SanitizedTvSearchString, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(MTeamTpRequestType.Normal, searchCriteria, searchCriteria.SanitizedSearchTerm));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(MTeamTpRequestType.Normal, searchCriteria, searchCriteria.SanitizedSearchTerm));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(MTeamTpRequestType.Normal, searchCriteria, searchCriteria.SanitizedSearchTerm));
|
||||
pageableRequests.Add(GetPagedRequests(MTeamTpRequestType.Adult, searchCriteria, searchCriteria.SanitizedSearchTerm));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(MTeamTpRequestType mTeamTpRequestType, SearchCriteriaBase searchCriteria, string searchTerm, string imdbId = null)
|
||||
{
|
||||
var categoryMapping = _capabilities.Categories
|
||||
.MapTorznabCapsToTrackers(searchCriteria.Categories)
|
||||
.Select(int.Parse)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
var adultCategories = categoryMapping.Where(c => _trackerAdultCategories.Contains(c)).ToList();
|
||||
var normalCategories = categoryMapping.Except(adultCategories).ToList();
|
||||
|
||||
switch (mTeamTpRequestType)
|
||||
{
|
||||
case MTeamTpRequestType.Adult when adultCategories.Any():
|
||||
yield return BuildSearchRequest(mTeamTpRequestType, adultCategories, searchTerm, imdbId);
|
||||
break;
|
||||
case MTeamTpRequestType.Normal when !categoryMapping.Any() || normalCategories.Any():
|
||||
yield return BuildSearchRequest(mTeamTpRequestType, normalCategories, searchTerm, imdbId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private IndexerRequest BuildSearchRequest(MTeamTpRequestType requestType, IEnumerable<int> categoryMapping, string searchTerm, string imdbId)
|
||||
{
|
||||
var request = new HttpRequestBuilder(_settings.BaseUrl)
|
||||
.Resource("/api/torrent/search")
|
||||
.SetHeader("x-api-key", _settings.ApiKey)
|
||||
.Accept(HttpAccept.Json)
|
||||
.Post()
|
||||
.Build();
|
||||
|
||||
var query = new MTeamTpApiSearchQuery
|
||||
{
|
||||
Mode = requestType,
|
||||
Categories = categoryMapping?.Select(x => x.ToString()).ToArray() ?? Array.Empty<string>(),
|
||||
PageNumber = 1,
|
||||
PageSize = 100
|
||||
};
|
||||
|
||||
if (imdbId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
query.Imdb = imdbId.Trim();
|
||||
}
|
||||
|
||||
if (searchTerm.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
query.Keyword = searchTerm.Trim();
|
||||
}
|
||||
|
||||
if (_settings.FreeleechOnly)
|
||||
{
|
||||
query.Discount = "FREE";
|
||||
}
|
||||
|
||||
request.Headers.ContentType = "application/json";
|
||||
request.SetContent(query.ToJson());
|
||||
request.ContentSummary = query.ToJson(Formatting.None);
|
||||
request.Headers.UserAgent = MTeamTp.UserAgent;
|
||||
|
||||
return new IndexerRequest(request);
|
||||
}
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
public class MTeamTpParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly MTeamTpSettings _settings;
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
|
||||
public MTeamTpParser(MTeamTpSettings settings, IndexerCapabilitiesCategories categories)
|
||||
{
|
||||
_settings = settings;
|
||||
_categories = categories;
|
||||
}
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var httpResponse = indexerResponse.HttpResponse;
|
||||
|
||||
if (httpResponse.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request");
|
||||
}
|
||||
|
||||
if (!httpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value))
|
||||
{
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from indexer request, expected {HttpAccept.Json.Value}");
|
||||
}
|
||||
|
||||
if (!STJson.TryDeserialize<MTeamTpApiResponse>(indexerResponse.Content, out var jsonResponse))
|
||||
{
|
||||
throw new IndexerException(indexerResponse, "Invalid response received from M-Team, not a valid JSON");
|
||||
}
|
||||
|
||||
var releaseInfos = new List<ReleaseInfo>();
|
||||
|
||||
if (jsonResponse?.Data?.Torrents == null)
|
||||
{
|
||||
return releaseInfos;
|
||||
}
|
||||
|
||||
foreach (var torrent in jsonResponse.Data.Torrents)
|
||||
{
|
||||
var torrentId = int.Parse(torrent.Id);
|
||||
var infoUrl = $"{_settings.BaseUrl.TrimEnd('/')}/detail/{torrentId}";
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Guid = infoUrl,
|
||||
Title = CleanTitle(torrent.Name),
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = GetDownloadUrl(torrentId),
|
||||
Categories = _categories.MapTrackerCatToNewznab(torrent.Category),
|
||||
Description = torrent.Description,
|
||||
Files = int.Parse(torrent.NumFiles),
|
||||
Size = long.Parse(torrent.Size),
|
||||
Grabs = int.Parse(torrent.Status.TimesCompleted),
|
||||
Seeders = int.Parse(torrent.Status.Seeders),
|
||||
Peers = int.Parse(torrent.Status.Seeders) + int.Parse(torrent.Status.Leechers),
|
||||
DownloadVolumeFactor = torrent.Status.Discount.ToUpperInvariant() switch
|
||||
{
|
||||
"FREE" => 0,
|
||||
"_2X_FREE" => 0,
|
||||
"PERCENT_50" => 0.5,
|
||||
"_2X_PERCENT_50" => 0.5,
|
||||
"PERCENT_70" => 0.3,
|
||||
_ => 1
|
||||
},
|
||||
UploadVolumeFactor = torrent.Status.Discount.ToUpperInvariant() switch
|
||||
{
|
||||
"_2X_FREE" => 2,
|
||||
"_2X_PERCENT_50" => 2,
|
||||
_ => 1
|
||||
},
|
||||
MinimumRatio = 1,
|
||||
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))
|
||||
{
|
||||
release.PublishDate = publishDate;
|
||||
}
|
||||
|
||||
releaseInfos.Add(release);
|
||||
}
|
||||
|
||||
return releaseInfos
|
||||
.OrderByDescending(o => o.PublishDate)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private string GetDownloadUrl(int torrentId)
|
||||
{
|
||||
var url = new HttpUri(_settings.BaseUrl)
|
||||
.CombinePath("/api/torrent/genDlToken")
|
||||
.AddQueryParam("id", torrentId);
|
||||
|
||||
return url.FullUri;
|
||||
}
|
||||
|
||||
private static string CleanTitle(string title)
|
||||
{
|
||||
title = Regex.Replace(title, @"\s+", " ", RegexOptions.Compiled);
|
||||
|
||||
return title.Trim();
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
public class MTeamTpValidator : NoAuthSettingsValidator<MTeamTpSettings>
|
||||
{
|
||||
public MTeamTpValidator()
|
||||
{
|
||||
RuleFor(c => c.ApiKey).NotEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public class MTeamTpSettings : NoAuthTorrentBaseSettings
|
||||
{
|
||||
private static readonly MTeamTpValidator Validator = new ();
|
||||
|
||||
[FieldDefinition(2, Label = "ApiKey", HelpText = "IndexerMTeamTpSettingsApiKeyHelpText", Privacy = PrivacyLevel.ApiKey)]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "IndexerSettingsFreeleechOnly", Type = FieldType.Checkbox, HelpText = "IndexerMTeamTpSettingsFreeleechOnlyHelpText")]
|
||||
public bool FreeleechOnly { get; set; }
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
|
||||
internal enum MTeamTpRequestType
|
||||
{
|
||||
Normal,
|
||||
Adult
|
||||
}
|
||||
|
||||
internal class MTeamTpApiSearchQuery
|
||||
{
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
public MTeamTpRequestType Mode { get; set; }
|
||||
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
public IEnumerable<string> Categories { get; set; }
|
||||
|
||||
public string Discount { get; set; }
|
||||
public string Imdb { get; set; }
|
||||
public string Keyword { get; set; }
|
||||
public int? PageNumber { get; set; }
|
||||
public int? PageSize { get; set; }
|
||||
}
|
||||
|
||||
internal class MTeamTpApiResponse
|
||||
{
|
||||
public MTeamTpApiData Data { get; set; }
|
||||
}
|
||||
|
||||
internal class MTeamTpApiData
|
||||
{
|
||||
[JsonPropertyName("data")]
|
||||
public IReadOnlyCollection<MTeamTpApiTorrent> Torrents { get; set; }
|
||||
}
|
||||
|
||||
internal class MTeamTpApiTorrent
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonPropertyName("smallDescr")]
|
||||
public string Description { get; set; }
|
||||
|
||||
public string Category { get; set; }
|
||||
|
||||
[JsonPropertyName("numfiles")]
|
||||
public string NumFiles { get; set; }
|
||||
|
||||
public string Imdb { get; set; }
|
||||
public string Size { get; set; }
|
||||
public MTeamTpApiReleaseStatus Status { get; set; }
|
||||
}
|
||||
|
||||
internal class MTeamTpApiReleaseStatus
|
||||
{
|
||||
public string CreatedDate { get; set; }
|
||||
public string Discount { get; set; }
|
||||
public string TimesCompleted { get; set; }
|
||||
public string Seeders { get; set; }
|
||||
public string Leechers { get; set; }
|
||||
}
|
||||
|
||||
internal class MTeamTpApiDownloadTokenResponse
|
||||
{
|
||||
public string Data { get; set; }
|
||||
}
|
||||
@@ -44,7 +44,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
|
||||
public string[] GetBaseUrlFromSettings()
|
||||
{
|
||||
if (Definition == null || Settings?.Categories == null)
|
||||
if (Definition == null || Settings?.Capabilities == null)
|
||||
{
|
||||
return new[] { "" };
|
||||
}
|
||||
@@ -61,16 +61,23 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
{
|
||||
var caps = new IndexerCapabilities();
|
||||
|
||||
if (Definition == null || Settings?.Categories == null)
|
||||
if (Definition == null || Settings?.Capabilities?.Categories == null)
|
||||
{
|
||||
return caps;
|
||||
}
|
||||
|
||||
foreach (var category in Settings.Categories)
|
||||
foreach (var category in Settings.Capabilities.Categories)
|
||||
{
|
||||
caps.Categories.AddCategoryMapping(category.Name, category);
|
||||
}
|
||||
|
||||
caps.SupportsRawSearch = Settings?.Capabilities?.SupportsRawSearch ?? false;
|
||||
caps.SearchParams = Settings?.Capabilities?.SearchParams ?? new List<SearchParam> { SearchParam.Q };
|
||||
caps.TvSearchParams = Settings?.Capabilities?.TvSearchParams ?? new List<TvSearchParam>();
|
||||
caps.MovieSearchParams = Settings?.Capabilities?.MovieSearchParams ?? new List<MovieSearchParam>();
|
||||
caps.MusicSearchParams = Settings?.Capabilities?.MusicSearchParams ?? new List<MusicSearchParam>();
|
||||
caps.BookSearchParams = Settings?.Capabilities?.BookSearchParams ?? new List<BookSearchParam>();
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
@@ -84,25 +91,24 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return GetDefinition("abNZB", GetSettings("https://abnzb.com"));
|
||||
yield return GetDefinition("altHUB", GetSettings("https://api.althub.co.za"));
|
||||
yield return GetDefinition("AnimeTosho (Usenet)", GetSettings("https://feed.animetosho.org"));
|
||||
yield return GetDefinition("DOGnzb", GetSettings("https://api.dognzb.cr"));
|
||||
yield return GetDefinition("DrunkenSlug", GetSettings("https://drunkenslug.com"));
|
||||
yield return GetDefinition("abNZB", GetSettings("https://abnzb.com"), categories: new[] { 2000, 3000, 4000, 5000, 6000, 7000, 8000 });
|
||||
yield return GetDefinition("altHUB", GetSettings("https://api.althub.co.za"), categories: new[] { 2000, 3000, 4000, 5000, 7000 });
|
||||
yield return GetDefinition("AnimeTosho (Usenet)", GetSettings("https://feed.animetosho.org"), categories: new[] { 2020, 5070 });
|
||||
yield return GetDefinition("DOGnzb", GetSettings("https://api.dognzb.cr"), categories: new[] { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 });
|
||||
yield return GetDefinition("DrunkenSlug", GetSettings("https://drunkenslug.com"), categories: new[] { 1000, 2000, 3000, 4000, 5000, 6000, 7000 });
|
||||
yield return GetDefinition("GingaDADDY", GetSettings("https://www.gingadaddy.com"));
|
||||
yield return GetDefinition("Miatrix", GetSettings("https://www.miatrix.com"));
|
||||
yield return GetDefinition("Newz-Complex", GetSettings("https://newz-complex.org/www"));
|
||||
yield return GetDefinition("Newz69", GetSettings("https://newz69.keagaming.com"));
|
||||
yield return GetDefinition("NinjaCentral", GetSettings("https://ninjacentral.co.za"));
|
||||
yield return GetDefinition("Nzb.su", GetSettings("https://api.nzb.su"));
|
||||
yield return GetDefinition("NZBCat", GetSettings("https://nzb.cat"));
|
||||
yield return GetDefinition("NZBFinder", GetSettings("https://nzbfinder.ws"));
|
||||
yield return GetDefinition("NZBgeek", GetSettings("https://api.nzbgeek.info"));
|
||||
yield return GetDefinition("NzbNoob", GetSettings("https://www.nzbnoob.com"));
|
||||
yield return GetDefinition("NZBNDX", GetSettings("https://www.nzbndx.com"));
|
||||
yield return GetDefinition("NzbPlanet", GetSettings("https://api.nzbplanet.net"));
|
||||
yield return GetDefinition("NZBStars", GetSettings("https://nzbstars.com"));
|
||||
yield return GetDefinition("Tabula Rasa", GetSettings("https://www.tabula-rasa.pw", apiPath: @"/api/v1/api"));
|
||||
yield return GetDefinition("Miatrix", GetSettings("https://www.miatrix.com"), categories: new[] { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 });
|
||||
yield return GetDefinition("Newz69", GetSettings("https://newz69.keagaming.com"), categories: new[] { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 });
|
||||
yield return GetDefinition("NinjaCentral", GetSettings("https://ninjacentral.co.za"), categories: new[] { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 });
|
||||
yield return GetDefinition("Nzb.su", GetSettings("https://api.nzb.su"), categories: new[] { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 });
|
||||
yield return GetDefinition("NZBCat", GetSettings("https://nzb.cat"), categories: new[] { 1000, 2000, 3000, 4000, 5000, 6000, 7000 });
|
||||
yield return GetDefinition("NZBFinder", GetSettings("https://nzbfinder.ws"), categories: new[] { 2000, 3000, 5000, 6000, 7000 });
|
||||
yield return GetDefinition("NZBgeek", GetSettings("https://api.nzbgeek.info"), categories: new[] { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 });
|
||||
yield return GetDefinition("NzbNoob", GetSettings("https://www.nzbnoob.com"), categories: new[] { 2000, 3000, 4000, 5000, 6000, 7000, 8000 });
|
||||
yield return GetDefinition("NZBNDX", GetSettings("https://www.nzbndx.com"), categories: new[] { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 });
|
||||
yield return GetDefinition("NzbPlanet", GetSettings("https://api.nzbplanet.net"), categories: new[] { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 });
|
||||
yield return GetDefinition("NZBStars", GetSettings("https://nzbstars.com"), categories: new[] { 1000, 2000, 3000, 4000, 5000, 6000, 7000 });
|
||||
yield return GetDefinition("Tabula Rasa", GetSettings("https://www.tabula-rasa.pw", apiPath: @"/api/v1/api"), categories: new[] { 1000, 2000, 3000, 4000, 5000, 6000, 7000 });
|
||||
yield return GetDefinition("Generic Newznab", GetSettings(""));
|
||||
}
|
||||
}
|
||||
@@ -113,8 +119,23 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
_capabilitiesProvider = capabilitiesProvider;
|
||||
}
|
||||
|
||||
private IndexerDefinition GetDefinition(string name, NewznabSettings settings)
|
||||
private IndexerDefinition GetDefinition(string name, NewznabSettings settings, IEnumerable<int> categories = null)
|
||||
{
|
||||
var caps = new IndexerCapabilities();
|
||||
|
||||
if (categories != null)
|
||||
{
|
||||
foreach (var categoryId in categories)
|
||||
{
|
||||
var mappedCat = NewznabStandardCategory.AllCats.FirstOrDefault(x => x.Id == categoryId);
|
||||
|
||||
if (mappedCat != null)
|
||||
{
|
||||
caps.Categories.AddCategoryMapping(mappedCat.Id, mappedCat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new IndexerDefinition
|
||||
{
|
||||
Enable = true,
|
||||
@@ -127,7 +148,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
SupportsSearch = SupportsSearch,
|
||||
SupportsRedirect = SupportsRedirect,
|
||||
SupportsPagination = SupportsPagination,
|
||||
Capabilities = Capabilities
|
||||
Capabilities = caps
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
{
|
||||
foreach (var param in xmlBasicSearch.Attribute("supportedParams").Value.Split(','))
|
||||
{
|
||||
if (Enum.TryParse(param, true, out SearchParam searchParam))
|
||||
if (Enum.TryParse(param, true, out SearchParam searchParam) && !capabilities.SearchParams.Contains(searchParam))
|
||||
{
|
||||
capabilities.SearchParams.AddIfNotNull(searchParam);
|
||||
}
|
||||
@@ -146,7 +146,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
{
|
||||
foreach (var param in xmlMovieSearch.Attribute("supportedParams").Value.Split(','))
|
||||
{
|
||||
if (Enum.TryParse(param, true, out MovieSearchParam searchParam))
|
||||
if (Enum.TryParse(param, true, out MovieSearchParam searchParam) && !capabilities.MovieSearchParams.Contains(searchParam))
|
||||
{
|
||||
capabilities.MovieSearchParams.AddIfNotNull(searchParam);
|
||||
}
|
||||
@@ -166,7 +166,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
{
|
||||
foreach (var param in xmlTvSearch.Attribute("supportedParams").Value.Split(','))
|
||||
{
|
||||
if (Enum.TryParse(param, true, out TvSearchParam searchParam))
|
||||
if (Enum.TryParse(param, true, out TvSearchParam searchParam) && !capabilities.TvSearchParams.Contains(searchParam))
|
||||
{
|
||||
capabilities.TvSearchParams.AddIfNotNull(searchParam);
|
||||
}
|
||||
@@ -186,7 +186,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
{
|
||||
foreach (var param in xmlAudioSearch.Attribute("supportedParams").Value.Split(','))
|
||||
{
|
||||
if (Enum.TryParse(param, true, out MusicSearchParam searchParam))
|
||||
if (Enum.TryParse(param, true, out MusicSearchParam searchParam) && !capabilities.MusicSearchParams.Contains(searchParam))
|
||||
{
|
||||
capabilities.MusicSearchParams.AddIfNotNull(searchParam);
|
||||
}
|
||||
@@ -206,7 +206,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
{
|
||||
foreach (var param in xmlBookSearch.Attribute("supportedParams").Value.Split(','))
|
||||
{
|
||||
if (Enum.TryParse(param, true, out BookSearchParam searchParam))
|
||||
if (Enum.TryParse(param, true, out BookSearchParam searchParam) && !capabilities.BookSearchParams.Contains(searchParam))
|
||||
{
|
||||
capabilities.BookSearchParams.AddIfNotNull(searchParam);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Newznab;
|
||||
|
||||
public class NewznabCapabilitiesSettings
|
||||
{
|
||||
public bool SupportsRawSearch { get; set; }
|
||||
|
||||
public List<SearchParam> SearchParams { get; set; }
|
||||
|
||||
public List<TvSearchParam> TvSearchParams { get; set; }
|
||||
|
||||
public List<MovieSearchParam> MovieSearchParams { get; set; }
|
||||
|
||||
public List<MusicSearchParam> MusicSearchParams { get; set; }
|
||||
|
||||
public List<BookSearchParam> BookSearchParams { get; set; }
|
||||
|
||||
public List<IndexerCategory> Categories { get; set; }
|
||||
|
||||
public NewznabCapabilitiesSettings()
|
||||
{
|
||||
}
|
||||
|
||||
public NewznabCapabilitiesSettings(IndexerCapabilities capabilities)
|
||||
{
|
||||
SupportsRawSearch = capabilities?.SupportsRawSearch ?? false;
|
||||
SearchParams = capabilities?.SearchParams;
|
||||
TvSearchParams = capabilities?.TvSearchParams;
|
||||
MovieSearchParams = capabilities?.MovieSearchParams;
|
||||
MusicSearchParams = capabilities?.MusicSearchParams;
|
||||
BookSearchParams = capabilities?.BookSearchParams;
|
||||
Categories = capabilities?.Categories.GetTorznabCategoryList();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using FluentValidation;
|
||||
@@ -76,7 +75,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
[FieldDefinition(7)]
|
||||
public IndexerBaseSettings BaseSettings { get; set; } = new ();
|
||||
|
||||
public List<IndexerCategory> Categories { get; set; }
|
||||
public NewznabCapabilitiesSettings Capabilities { get; set; }
|
||||
|
||||
// Field 8 is used by TorznabSettings MinimumSeeders
|
||||
// If you need to add another field here, update TorznabSettings as well and this comment
|
||||
|
||||
@@ -276,6 +276,12 @@ 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)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var id = torrent.TorrentId;
|
||||
|
||||
var title = GetTitle(result, torrent);
|
||||
@@ -285,7 +291,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
Guid = infoUrl,
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = GetDownloadUrl(id, torrent.CanUseToken),
|
||||
DownloadUrl = GetDownloadUrl(id, !torrent.IsFreeLeech && !torrent.IsNeutralLeech && !torrent.IsPersonalFreeLeech),
|
||||
Title = WebUtility.HtmlDecode(title),
|
||||
Artist = WebUtility.HtmlDecode(result.Artist),
|
||||
Album = WebUtility.HtmlDecode(result.GroupName),
|
||||
@@ -320,16 +326,22 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
// Non-Audio files are formatted a little differently (1:1 for group and torrents)
|
||||
else
|
||||
{
|
||||
// skip releases that cannot be used with freeleech tokens when the option is enabled
|
||||
if (_settings.UseFreeleechToken && !result.CanUseToken)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var id = result.TorrentId;
|
||||
var infoUrl = GetInfoUrl(result.GroupId, id);
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Guid = infoUrl,
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = GetDownloadUrl(id, !result.IsFreeLeech && !result.IsNeutralLeech && !result.IsPersonalFreeLeech),
|
||||
Title = WebUtility.HtmlDecode(result.GroupName),
|
||||
Size = long.Parse(result.Size),
|
||||
DownloadUrl = GetDownloadUrl(id, result.CanUseToken),
|
||||
InfoUrl = infoUrl,
|
||||
Seeders = int.Parse(result.Seeders),
|
||||
Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders),
|
||||
PublishDate = long.TryParse(result.GroupTime, out var num) ? DateTimeOffset.FromUnixTimeSeconds(num).UtcDateTime : DateTimeUtil.FromFuzzyTime(result.GroupTime),
|
||||
|
||||
@@ -247,6 +247,12 @@ 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)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip non-freeload results when freeload only is set
|
||||
if (_settings.FreeloadOnly && !torrent.IsFreeload)
|
||||
{
|
||||
@@ -262,7 +268,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
Guid = infoUrl,
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = GetDownloadUrl(id, torrent.CanUseToken && !torrent.IsFreeload),
|
||||
DownloadUrl = GetDownloadUrl(id, !torrent.IsFreeLeech && !torrent.IsNeutralLeech && !torrent.IsFreeload && !torrent.IsPersonalFreeLeech),
|
||||
Title = WebUtility.HtmlDecode(title),
|
||||
Artist = WebUtility.HtmlDecode(result.Artist),
|
||||
Album = WebUtility.HtmlDecode(result.GroupName),
|
||||
@@ -297,6 +303,12 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
// Non-Audio files are formatted a little differently (1:1 for group and torrents)
|
||||
else
|
||||
{
|
||||
// skip releases that cannot be used with freeleech tokens when the option is enabled
|
||||
if (_settings.UseFreeleechToken && !result.CanUseToken)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip non-freeload results when freeload only is set
|
||||
if (_settings.FreeloadOnly && !result.IsFreeload)
|
||||
{
|
||||
@@ -309,10 +321,10 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Guid = infoUrl,
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = GetDownloadUrl(id, !result.IsFreeLeech && !result.IsNeutralLeech && !result.IsFreeload && !result.IsPersonalFreeLeech),
|
||||
Title = WebUtility.HtmlDecode(result.GroupName),
|
||||
Size = long.Parse(result.Size),
|
||||
DownloadUrl = GetDownloadUrl(id, result.CanUseToken && !result.IsFreeload),
|
||||
InfoUrl = infoUrl,
|
||||
Seeders = int.Parse(result.Seeders),
|
||||
Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders),
|
||||
PublishDate = DateTimeOffset.FromUnixTimeSeconds(ParseUtil.CoerceLong(result.GroupTime)).UtcDateTime,
|
||||
|
||||
@@ -23,14 +23,14 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
public class RuTracker : TorrentIndexerBase<RuTrackerSettings>
|
||||
{
|
||||
public override string Name => "RuTracker";
|
||||
public override string Name => "RuTracker.org";
|
||||
public override string[] IndexerUrls => new[]
|
||||
{
|
||||
"https://rutracker.org/",
|
||||
"https://rutracker.net/",
|
||||
"https://rutracker.nl/"
|
||||
};
|
||||
public override string Description => "RuTracker is a Semi-Private Russian torrent site with a thriving file-sharing community";
|
||||
public override string Description => "RuTracker.org is a Semi-Private Russian torrent 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;
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new SceneHDRequestGenerator() { Settings = Settings, Capabilities = Capabilities };
|
||||
return new SceneHDRequestGenerator(Settings, Capabilities);
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
@@ -88,38 +88,41 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public class SceneHDRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
public SceneHDSettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
public string BaseUrl { get; set; }
|
||||
private readonly SceneHDSettings _settings;
|
||||
private readonly IndexerCapabilities _capabilities;
|
||||
|
||||
public SceneHDRequestGenerator(SceneHDSettings settings, IndexerCapabilities capabilities)
|
||||
{
|
||||
_settings = settings;
|
||||
_capabilities = capabilities;
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories, string imdbId = null)
|
||||
{
|
||||
var search = new[] { imdbId, term };
|
||||
|
||||
var qc = new NameValueCollection
|
||||
var parameters = new NameValueCollection
|
||||
{
|
||||
{ "api", "" },
|
||||
{ "passkey", Settings.Passkey },
|
||||
{ "passkey", _settings.Passkey },
|
||||
{ "search", string.Join(" ", search.Where(s => s.IsNotNullOrWhiteSpace())) }
|
||||
};
|
||||
|
||||
foreach (var cat in Capabilities.Categories.MapTorznabCapsToTrackers(categories))
|
||||
if (categories?.Length > 0)
|
||||
{
|
||||
qc.Add("categories[" + cat + "]", "1");
|
||||
parameters.Add("cat", _capabilities.Categories.MapTorznabCapsToTrackers(categories).Distinct().Join(","));
|
||||
}
|
||||
|
||||
var searchUrl = string.Format("{0}/browse.php?{1}", Settings.BaseUrl.TrimEnd('/'), qc.GetQueryString());
|
||||
var searchUrl = $"{_settings.BaseUrl.TrimEnd('/')}/browse.php?{parameters.GetQueryString()}";
|
||||
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Json);
|
||||
|
||||
yield return request;
|
||||
yield return new IndexerRequest(searchUrl, HttpAccept.Json);
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -128,7 +131,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -137,7 +140,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedTvSearchString}", searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -146,7 +149,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -155,7 +158,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
@@ -104,6 +104,7 @@ public class SecretCinemaParser : IParseIndexerResponse
|
||||
foreach (var torrent in result.Torrents)
|
||||
{
|
||||
var id = torrent.TorrentId;
|
||||
var infoUrl = GetInfoUrl(result.GroupId, id);
|
||||
|
||||
// in SC movies, artist=director and GroupName=title
|
||||
var artist = WebUtility.HtmlDecode(result.Artist);
|
||||
@@ -112,15 +113,15 @@ public class SecretCinemaParser : IParseIndexerResponse
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Guid = $"SecretCinema-{id}",
|
||||
Guid = infoUrl,
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = GetDownloadUrl(id),
|
||||
Title = title,
|
||||
Container = torrent.Encoding,
|
||||
Files = torrent.FileCount,
|
||||
Grabs = torrent.Snatches,
|
||||
Codec = torrent.Format,
|
||||
Size = long.Parse(torrent.Size),
|
||||
DownloadUrl = GetDownloadUrl(id),
|
||||
InfoUrl = GetInfoUrl(result.GroupId, id),
|
||||
Seeders = int.Parse(torrent.Seeders),
|
||||
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
|
||||
PublishDate = new DateTimeOffset(time, TimeSpan.FromHours(2)).UtcDateTime,
|
||||
@@ -143,7 +144,12 @@ public class SecretCinemaParser : IParseIndexerResponse
|
||||
{
|
||||
// Remove director from title
|
||||
// SC API returns no more useful information than this
|
||||
release.Title = $"{title} ({result.GroupYear}) {torrent.Media}";
|
||||
release.Title = $"{title} ({result.GroupYear}) {torrent.Media}".Trim();
|
||||
|
||||
if (torrent.RemasterTitle.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
release.Title += $" [{torrent.RemasterTitle.Trim()}]";
|
||||
}
|
||||
|
||||
// Replace media formats with standards
|
||||
release.Title = Regex.Replace(release.Title, @"\bBDMV\b", "COMPLETE BLURAY", RegexOptions.IgnoreCase);
|
||||
@@ -168,15 +174,16 @@ public class SecretCinemaParser : IParseIndexerResponse
|
||||
else
|
||||
{
|
||||
var id = result.TorrentId;
|
||||
var infoUrl = GetInfoUrl(result.GroupId, id);
|
||||
var groupName = WebUtility.HtmlDecode(result.GroupName);
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Guid = $"SecretCinema-{id}",
|
||||
Guid = infoUrl,
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = GetDownloadUrl(id),
|
||||
Title = groupName,
|
||||
Size = long.Parse(result.Size),
|
||||
DownloadUrl = GetDownloadUrl(id),
|
||||
InfoUrl = GetInfoUrl(result.GroupId, id),
|
||||
Seeders = int.Parse(result.Seeders),
|
||||
Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders),
|
||||
Files = result.FileCount,
|
||||
@@ -209,7 +216,7 @@ public class SecretCinemaParser : IParseIndexerResponse
|
||||
|
||||
private bool IsAnyMovieCategory(ICollection<IndexerCategory> category)
|
||||
{
|
||||
return category.Contains(NewznabStandardCategory.Movies) || NewznabStandardCategory.Movies.SubCategories.Any(subCat => category.Contains(subCat));
|
||||
return category.Contains(NewznabStandardCategory.Movies) || NewznabStandardCategory.Movies.SubCategories.Any(category.Contains);
|
||||
}
|
||||
|
||||
private string GetDownloadUrl(int torrentId)
|
||||
|
||||
@@ -17,6 +17,7 @@ using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
[Obsolete("Site unusable due to lack of new releases")]
|
||||
public class Shizaproject : TorrentIndexerBase<NoAuthTorrentBaseSettings>
|
||||
{
|
||||
public override string Name => "ShizaProject";
|
||||
|
||||
@@ -82,49 +82,13 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetSearchRequests(string term)
|
||||
{
|
||||
var searchUrl = $"{_settings.BaseUrl.TrimEnd('/')}/api/?";
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
var queryParameters = new NameValueCollection
|
||||
{
|
||||
{ "f", "search" },
|
||||
{ "tz", "UTC" },
|
||||
{ "s", searchTerm }
|
||||
};
|
||||
|
||||
var request = new IndexerRequest(searchUrl + queryParameters.GetQueryString(), HttpAccept.Json);
|
||||
|
||||
yield return request;
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetRssRequest()
|
||||
{
|
||||
var searchUrl = $"{_settings.BaseUrl.TrimEnd('/')}/api/?";
|
||||
|
||||
var queryParameters = new NameValueCollection
|
||||
{
|
||||
{ "f", "latest" },
|
||||
{ "tz", "UTC" }
|
||||
};
|
||||
|
||||
var request = new IndexerRequest(searchUrl + queryParameters.GetQueryString(), HttpAccept.Json);
|
||||
|
||||
yield return request;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
return new IndexerPageableRequestChain();
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetSearchRequests(searchCriteria.SanitizedSearchTerm, searchCriteria));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
||||
@@ -136,31 +100,69 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(searchCriteria.IsRssSearch
|
||||
? GetRssRequest()
|
||||
: GetSearchRequests(searchCriteria.SanitizedTvSearchString));
|
||||
var searchTerm = searchCriteria.SanitizedSearchTerm.Trim();
|
||||
|
||||
// Only include season > 1 in searchTerm, format as S2 rather than S02
|
||||
if (searchCriteria.Season is > 1)
|
||||
{
|
||||
searchTerm += $" S{searchCriteria.Season}";
|
||||
}
|
||||
|
||||
if (int.TryParse(searchCriteria.Episode, out var episode) && episode > 0)
|
||||
{
|
||||
searchTerm += $" {episode:00}";
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetSearchRequests(searchTerm, searchCriteria));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
return pageableRequests;
|
||||
return new IndexerPageableRequestChain();
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(searchCriteria.IsRssSearch
|
||||
? GetRssRequest()
|
||||
: GetSearchRequests(searchCriteria.SanitizedSearchTerm));
|
||||
pageableRequests.Add(GetSearchRequests(searchCriteria.SanitizedSearchTerm, searchCriteria));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
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" }
|
||||
};
|
||||
|
||||
if (searchCriteria.IsRssSearch)
|
||||
{
|
||||
queryParameters.Set("f", "latest");
|
||||
}
|
||||
else
|
||||
{
|
||||
queryParameters.Set("f", "search");
|
||||
queryParameters.Set("s", searchTerm);
|
||||
}
|
||||
|
||||
var searchUrl = $"{_settings.BaseUrl.TrimEnd('/')}/api/?{queryParameters.GetQueryString()}";
|
||||
|
||||
yield return new IndexerRequest(searchUrl, HttpAccept.Json);
|
||||
}
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace NzbDrone.Core.Indexers.Torznab
|
||||
|
||||
public string[] GetBaseUrlFromSettings()
|
||||
{
|
||||
if (Definition == null || Settings?.Categories == null)
|
||||
if (Definition == null || Settings?.Capabilities == null)
|
||||
{
|
||||
return new[] { "" };
|
||||
}
|
||||
@@ -61,16 +61,23 @@ namespace NzbDrone.Core.Indexers.Torznab
|
||||
{
|
||||
var caps = new IndexerCapabilities();
|
||||
|
||||
if (Definition == null || Settings?.Categories == null)
|
||||
if (Definition == null || Settings?.Capabilities?.Categories == null)
|
||||
{
|
||||
return caps;
|
||||
}
|
||||
|
||||
foreach (var category in Settings.Categories)
|
||||
foreach (var category in Settings.Capabilities.Categories)
|
||||
{
|
||||
caps.Categories.AddCategoryMapping(category.Name, category);
|
||||
}
|
||||
|
||||
caps.SupportsRawSearch = Settings?.Capabilities?.SupportsRawSearch ?? false;
|
||||
caps.SearchParams = Settings?.Capabilities?.SearchParams ?? new List<SearchParam> { SearchParam.Q };
|
||||
caps.TvSearchParams = Settings?.Capabilities?.TvSearchParams ?? new List<TvSearchParam>();
|
||||
caps.MovieSearchParams = Settings?.Capabilities?.MovieSearchParams ?? new List<MovieSearchParam>();
|
||||
caps.MusicSearchParams = Settings?.Capabilities?.MusicSearchParams ?? new List<MusicSearchParam>();
|
||||
caps.BookSearchParams = Settings?.Capabilities?.BookSearchParams ?? new List<BookSearchParam>();
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
@@ -84,8 +91,8 @@ namespace NzbDrone.Core.Indexers.Torznab
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return GetDefinition("AnimeTosho", "Anime NZB/DDL mirror", settings: GetSettings("https://feed.animetosho.org"));
|
||||
yield return GetDefinition("MoreThanTV", "Private torrent tracker for TV / MOVIES", settings: GetSettings("https://www.morethantv.me", apiPath: @"/api/torznab"));
|
||||
yield return GetDefinition("AnimeTosho", "Anime NZB/DDL mirror", settings: GetSettings("https://feed.animetosho.org"), categories: new[] { 2020, 5070 });
|
||||
yield return GetDefinition("MoreThanTV", "Private torrent tracker for TV / MOVIES", settings: GetSettings("https://www.morethantv.me", apiPath: @"/api/torznab"), categories: new[] { 2000, 5000 });
|
||||
yield return GetDefinition("Torrent Network", "Torrent Network (TN) is a GERMAN Private site for TV / MOVIES / GENERAL", language: "de-DE", settings: GetSettings("https://tntracker.org", apiPath: @"/api/torznab/api"));
|
||||
yield return GetDefinition("Generic Torznab", "A Newznab-like api for torrents.", settings: GetSettings(""));
|
||||
}
|
||||
@@ -97,8 +104,23 @@ namespace NzbDrone.Core.Indexers.Torznab
|
||||
_capabilitiesProvider = capabilitiesProvider;
|
||||
}
|
||||
|
||||
private IndexerDefinition GetDefinition(string name, string description, string language = null, TorznabSettings settings = null)
|
||||
private IndexerDefinition GetDefinition(string name, string description, string language = null, TorznabSettings settings = null, IEnumerable<int> categories = null)
|
||||
{
|
||||
var caps = new IndexerCapabilities();
|
||||
|
||||
if (categories != null)
|
||||
{
|
||||
foreach (var categoryId in categories)
|
||||
{
|
||||
var mappedCat = NewznabStandardCategory.AllCats.FirstOrDefault(x => x.Id == categoryId);
|
||||
|
||||
if (mappedCat != null)
|
||||
{
|
||||
caps.Categories.AddCategoryMapping(mappedCat.Id, mappedCat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new IndexerDefinition
|
||||
{
|
||||
Enable = true,
|
||||
@@ -112,7 +134,7 @@ namespace NzbDrone.Core.Indexers.Torznab
|
||||
SupportsSearch = SupportsSearch,
|
||||
SupportsRedirect = SupportsRedirect,
|
||||
SupportsPagination = SupportsPagination,
|
||||
Capabilities = Capabilities
|
||||
Capabilities = caps
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -120,6 +120,7 @@ public class XSpeeds : TorrentIndexerBase<XSpeedsSettings>
|
||||
caps.Categories.AddCategoryMapping(113, NewznabStandardCategory.TVAnime, "Anime Boxsets");
|
||||
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(68, NewznabStandardCategory.MoviesOther, "Cams/TS");
|
||||
@@ -154,7 +155,7 @@ public class XSpeeds : TorrentIndexerBase<XSpeedsSettings>
|
||||
caps.Categories.AddCategoryMapping(13, NewznabStandardCategory.Audio, "Music");
|
||||
caps.Categories.AddCategoryMapping(135, NewznabStandardCategory.AudioLossless, "Music/FLAC");
|
||||
caps.Categories.AddCategoryMapping(136, NewznabStandardCategory.Audio, "Music Boxset");
|
||||
caps.Categories.AddCategoryMapping(15, NewznabStandardCategory.AudioVideo, "Music Videos");
|
||||
caps.Categories.AddCategoryMapping(148, NewznabStandardCategory.AudioVideo, "Music Videos");
|
||||
caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.Other, "Other");
|
||||
caps.Categories.AddCategoryMapping(125, NewznabStandardCategory.Other, "Other/Pictures");
|
||||
caps.Categories.AddCategoryMapping(54, NewznabStandardCategory.TVOther, "Soaps");
|
||||
@@ -168,6 +169,7 @@ public class XSpeeds : TorrentIndexerBase<XSpeedsSettings>
|
||||
caps.Categories.AddCategoryMapping(86, NewznabStandardCategory.TVSport, "Sports/MotorSports");
|
||||
caps.Categories.AddCategoryMapping(89, NewznabStandardCategory.TVSport, "Sports/Olympics");
|
||||
caps.Categories.AddCategoryMapping(126, NewznabStandardCategory.TV, "TV");
|
||||
caps.Categories.AddCategoryMapping(149, NewznabStandardCategory.TV, "TV Specials");
|
||||
caps.Categories.AddCategoryMapping(127, NewznabStandardCategory.TVUHD, "TV 4K");
|
||||
caps.Categories.AddCategoryMapping(129, NewznabStandardCategory.TVHD, "TV HD");
|
||||
caps.Categories.AddCategoryMapping(130, NewznabStandardCategory.TVHD, "TV HEVC");
|
||||
|
||||
@@ -18,6 +18,8 @@ using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using Polly;
|
||||
using Polly.Retry;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
@@ -28,6 +30,38 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
protected readonly IIndexerHttpClient _httpClient;
|
||||
protected readonly IEventAggregator _eventAggregator;
|
||||
|
||||
protected ResiliencePipeline<HttpResponse> RetryStrategy => new ResiliencePipelineBuilder<HttpResponse>()
|
||||
.AddRetry(new RetryStrategyOptions<HttpResponse>
|
||||
{
|
||||
ShouldHandle = static args => args.Outcome switch
|
||||
{
|
||||
{ Result.HasHttpServerError: true } => PredicateResult.True(),
|
||||
{ Result.StatusCode: HttpStatusCode.RequestTimeout } => PredicateResult.True(),
|
||||
_ => PredicateResult.False()
|
||||
},
|
||||
Delay = RateLimit,
|
||||
MaxRetryAttempts = 2,
|
||||
BackoffType = DelayBackoffType.Exponential,
|
||||
UseJitter = true,
|
||||
OnRetry = args =>
|
||||
{
|
||||
var exception = args.Outcome.Exception;
|
||||
|
||||
if (exception is not null)
|
||||
{
|
||||
_logger.Warn(exception, "Request for {0} failed with exception '{1}'. Retrying in {2}s.", Definition.Name, exception.Message, args.RetryDelay.TotalSeconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Warn("Request for {0} failed with status {1}. Retrying in {2}s.", Definition.Name, args.Outcome.Result?.StatusCode, args.RetryDelay.TotalSeconds);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
})
|
||||
.Build();
|
||||
|
||||
public IDictionary<string, string> Cookies { get; set; }
|
||||
|
||||
public override bool SupportsRss => true;
|
||||
@@ -363,7 +397,7 @@ namespace NzbDrone.Core.Indexers
|
||||
}
|
||||
}
|
||||
|
||||
releases.AddRange(pagedReleases.Where(IsValidRelease));
|
||||
releases.AddRange(pagedReleases.Where(r => IsValidRelease(r, searchCriteria.InteractiveSearch)));
|
||||
}
|
||||
|
||||
if (releases.Any())
|
||||
@@ -469,7 +503,7 @@ namespace NzbDrone.Core.Indexers
|
||||
return Capabilities ?? ((IndexerDefinition)Definition).Capabilities;
|
||||
}
|
||||
|
||||
protected virtual bool IsValidRelease(ReleaseInfo release)
|
||||
protected virtual bool IsValidRelease(ReleaseInfo release, bool interactiveSearch = false)
|
||||
{
|
||||
if (release.Title.IsNullOrWhiteSpace())
|
||||
{
|
||||
@@ -478,6 +512,26 @@ namespace NzbDrone.Core.Indexers
|
||||
return false;
|
||||
}
|
||||
|
||||
if (interactiveSearch)
|
||||
{
|
||||
// Show releases with issues in the interactive search
|
||||
return true;
|
||||
}
|
||||
|
||||
if (release.Size == null)
|
||||
{
|
||||
_logger.Warn("Invalid Release: '{0}' from indexer: {1}. No size provided.", release.Title, Definition.Name);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (release.Categories == null || !release.Categories.Any())
|
||||
{
|
||||
_logger.Warn("Invalid Release: '{0}' from indexer: {1}. No categories provided.", release.Title, Definition.Name);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -570,7 +624,9 @@ namespace NzbDrone.Core.Indexers
|
||||
request.HttpRequest.SuppressHttpError = true;
|
||||
request.HttpRequest.Encoding ??= Encoding;
|
||||
|
||||
var response = await _httpClient.ExecuteProxiedAsync(request.HttpRequest, Definition);
|
||||
var response = await RetryStrategy
|
||||
.ExecuteAsync(static async (state, _) => await state._httpClient.ExecuteProxiedAsync(state.HttpRequest, state.Definition), (_httpClient, request.HttpRequest, Definition))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// Check response to see if auth is needed, if needed try again
|
||||
if (CheckIfLoginNeeded(response))
|
||||
|
||||
@@ -171,10 +171,15 @@ namespace NzbDrone.Core.Indexers
|
||||
var splitRegex = new Regex("[^\\w]+");
|
||||
|
||||
// split search term to individual terms for less aggressive filtering, filter common terms
|
||||
var terms = splitRegex.Split(searchCriteria.SearchTerm).Where(t => t.IsNotNullOrWhiteSpace() && t.Length > 1 && !commonWords.ContainsIgnoreCase(t));
|
||||
var terms = splitRegex.Split(searchCriteria.SearchTerm).Where(t => t.IsNotNullOrWhiteSpace() && t.Length > 1 && !commonWords.ContainsIgnoreCase(t)).ToArray();
|
||||
|
||||
// check in title and description for any term searched for
|
||||
releases = releases.Where(r => terms.Any(t => (r.Title.IsNotNullOrWhiteSpace() && r.Title.ContainsIgnoreCase(t)) || (r.Description.IsNotNullOrWhiteSpace() && r.Description.ContainsIgnoreCase(t)))).ToList();
|
||||
releases = releases.Where(r =>
|
||||
{
|
||||
var matches = terms.Where(t => (r.Title.IsNotNullOrWhiteSpace() && r.Title.ContainsIgnoreCase(t)) || (r.Description.IsNotNullOrWhiteSpace() && r.Description.ContainsIgnoreCase(t)));
|
||||
|
||||
return terms.Length > 1 ? matches.Skip(1).Any() : matches.Any();
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
return releases;
|
||||
|
||||
@@ -208,7 +208,7 @@ namespace NzbDrone.Core.Indexers
|
||||
definition.Description ??= provider.Description;
|
||||
definition.Encoding = provider.Encoding;
|
||||
definition.Language ??= provider.Language;
|
||||
definition.Capabilities = provider.Capabilities;
|
||||
definition.Capabilities ??= provider.Capabilities;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +284,9 @@ namespace NzbDrone.Core.Indexers
|
||||
if (definition.Enable && definition.Implementation is nameof(Newznab.Newznab) or nameof(Torznab.Torznab))
|
||||
{
|
||||
var settings = (NewznabSettings)definition.Settings;
|
||||
settings.Categories = _newznabCapabilitiesProvider.GetCapabilities(settings, definition)?.Categories.GetTorznabCategoryList();
|
||||
var capabilities = _newznabCapabilitiesProvider.GetCapabilities(settings, definition);
|
||||
|
||||
settings.Capabilities = new NewznabCapabilitiesSettings(capabilities);
|
||||
}
|
||||
|
||||
if (definition.Implementation == nameof(Cardigann))
|
||||
@@ -304,7 +306,9 @@ namespace NzbDrone.Core.Indexers
|
||||
if (definition.Enable && definition.Implementation is nameof(Newznab.Newznab) or nameof(Torznab.Torznab))
|
||||
{
|
||||
var settings = (NewznabSettings)definition.Settings;
|
||||
settings.Categories = _newznabCapabilitiesProvider.GetCapabilities(settings, definition)?.Categories.GetTorznabCategoryList();
|
||||
var capabilities = _newznabCapabilitiesProvider.GetCapabilities(settings, definition);
|
||||
|
||||
settings.Capabilities = new NewznabCapabilitiesSettings(capabilities);
|
||||
}
|
||||
|
||||
if (definition.Implementation == nameof(Cardigann))
|
||||
|
||||
@@ -63,8 +63,5 @@ 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 = "IndexerSettingsRejectBlocklistedTorrentHashes", HelpText = "IndexerSettingsRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
|
||||
public bool RejectBlocklistedTorrentHashesWhileGrabbing { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"DeleteDownloadClientMessageText": "هل أنت متأكد أنك تريد حذف برنامج التنزيل \"{0}\"؟",
|
||||
"Filename": "اسم الملف",
|
||||
"HomePage": "الصفحة الرئيسية",
|
||||
"IndexerProxyStatusCheckSingleClientMessage": "المفهرسات غير متاحة بسبب الإخفاقات: {0}",
|
||||
"IndexerProxyStatusUnavailableHealthCheckMessage": "المفهرسات غير متاحة بسبب الإخفاقات: {indexerProxyNames}",
|
||||
"Language": "لغة",
|
||||
"Usenet": "يوزنت",
|
||||
"MovieIndexScrollTop": "فهرس الفيلم: قم بالتمرير لأعلى",
|
||||
@@ -47,8 +47,8 @@
|
||||
"Protocol": "بروتوكول",
|
||||
"Analytics": "تحليلات",
|
||||
"ProxyBypassFilterHelpText": "استخدم \"،\" كفاصل ، و \"*.\" كحرف بدل للنطاقات الفرعية",
|
||||
"ProxyCheckBadRequestMessage": "فشل اختبار الوكيل. رمز الحالة: {0}",
|
||||
"ProxyCheckFailedToTestMessage": "فشل اختبار الوكيل: {0}",
|
||||
"ProxyBadRequestHealthCheckMessage": "فشل اختبار الوكيل. رمز الحالة: {statusCode}",
|
||||
"ProxyFailedToTestHealthCheckMessage": "فشل اختبار الوكيل: {url}",
|
||||
"ProxyPasswordHelpText": "ما عليك سوى إدخال اسم مستخدم وكلمة مرور إذا كان أحدهما مطلوبًا. اتركها فارغة وإلا.",
|
||||
"ProxyUsernameHelpText": "ما عليك سوى إدخال اسم مستخدم وكلمة مرور إذا كان أحدهما مطلوبًا. اتركها فارغة وإلا.",
|
||||
"Queue": "طابور",
|
||||
@@ -68,7 +68,7 @@
|
||||
"UnableToAddANewIndexerPleaseTryAgain": "غير قادر على إضافة مفهرس جديد ، يرجى المحاولة مرة أخرى.",
|
||||
"UnableToLoadBackups": "تعذر تحميل النسخ الاحتياطية",
|
||||
"UnsavedChanges": "التغييرات غير المحفوظة",
|
||||
"UpdateCheckUINotWritableMessage": "لا يمكن تثبيت التحديث لأن مجلد واجهة المستخدم '{0}' غير قابل للكتابة بواسطة المستخدم '{1}'",
|
||||
"UpdateUiNotWritableHealthCheckMessage": "لا يمكن تثبيت التحديث لأن مجلد واجهة المستخدم '{uiFolder}' غير قابل للكتابة بواسطة المستخدم '{userName}'",
|
||||
"UpdateScriptPathHelpText": "المسار إلى برنامج نصي مخصص يأخذ حزمة تحديث مستخرجة ويتعامل مع ما تبقى من عملية التحديث",
|
||||
"Username": "اسم المستخدم",
|
||||
"Warn": "حذر",
|
||||
@@ -76,7 +76,7 @@
|
||||
"EnableInteractiveSearch": "تمكين البحث التفاعلي",
|
||||
"Source": "مصدر",
|
||||
"SSLCertPassword": "كلمة مرور شهادة SSL",
|
||||
"UpdateCheckStartupNotWritableMessage": "لا يمكن تثبيت التحديث لأن مجلد بدء التشغيل \"{0}\" غير قابل للكتابة بواسطة المستخدم \"{1}\".",
|
||||
"UpdateStartupNotWritableHealthCheckMessage": "لا يمكن تثبيت التحديث لأن مجلد بدء التشغيل \"{startupFolder}\" غير قابل للكتابة بواسطة المستخدم \"{userName}\".",
|
||||
"UpdateMechanismHelpText": "استخدم المحدث أو البرنامج النصي المدمج في {appName}",
|
||||
"AppDataDirectory": "دليل AppData",
|
||||
"ConnectSettings": "ربط الإعدادات",
|
||||
@@ -87,8 +87,8 @@
|
||||
"Details": "تفاصيل",
|
||||
"Donations": "التبرعات",
|
||||
"DownloadClient": "تحميل العميل",
|
||||
"IndexerStatusCheckAllClientMessage": "جميع المفهرسات غير متوفرة بسبب الفشل",
|
||||
"IndexerStatusCheckSingleClientMessage": "المفهرسات غير متاحة بسبب الإخفاقات: {0}",
|
||||
"IndexerStatusAllUnavailableHealthCheckMessage": "جميع المفهرسات غير متوفرة بسبب الفشل",
|
||||
"IndexerStatusUnavailableHealthCheckMessage": "المفهرسات غير متاحة بسبب الإخفاقات: {indexerNames}",
|
||||
"Info": "معلومات",
|
||||
"Interval": "فترة",
|
||||
"Manual": "كتيب",
|
||||
@@ -130,10 +130,10 @@
|
||||
"IncludeHealthWarningsHelpText": "قم بتضمين التحذيرات الصحية",
|
||||
"Indexer": "مفهرس",
|
||||
"IndexerFlags": "أعلام المفهرس",
|
||||
"IndexerLongTermStatusCheckAllClientMessage": "جميع المفهرسات غير متوفرة بسبب الفشل لأكثر من 6 ساعات",
|
||||
"IndexerLongTermStatusCheckSingleClientMessage": "المفهرسات غير متاحة بسبب الإخفاقات لأكثر من 6 ساعات: {0}",
|
||||
"IndexerLongTermStatusAllUnavailableHealthCheckMessage": "جميع المفهرسات غير متوفرة بسبب الفشل لأكثر من 6 ساعات",
|
||||
"IndexerLongTermStatusUnavailableHealthCheckMessage": "المفهرسات غير متاحة بسبب الإخفاقات لأكثر من 6 ساعات: {indexerNames}",
|
||||
"IndexerPriorityHelpText": "أولوية المفهرس من 1 (الأعلى) إلى 50 (الأدنى). الافتراضي: 25.",
|
||||
"IndexerProxyStatusCheckAllClientMessage": "جميع المفهرسات غير متوفرة بسبب الفشل",
|
||||
"IndexerProxyStatusAllUnavailableHealthCheckMessage": "جميع المفهرسات غير متوفرة بسبب الفشل",
|
||||
"NoChange": "لا تغيير",
|
||||
"NoLogFiles": "لا توجد ملفات سجل",
|
||||
"NoTagsHaveBeenAddedYet": "لم يتم إضافة أي علامات حتى الان",
|
||||
@@ -185,7 +185,7 @@
|
||||
"NetCore": ".شبكة",
|
||||
"Password": "كلمه السر",
|
||||
"Proxy": "الوكيل",
|
||||
"ProxyCheckResolveIpMessage": "فشل حل عنوان IP لمضيف الخادم الوكيل المكون {0}",
|
||||
"ProxyResolveIpHealthCheckMessage": "فشل حل عنوان IP لمضيف الخادم الوكيل المكون {proxyHostName}",
|
||||
"ProxyType": "نوع الوكيل",
|
||||
"ReleaseStatus": "حالة الإصدار",
|
||||
"Search": "بحث",
|
||||
@@ -212,8 +212,8 @@
|
||||
"DeleteIndexerProxyMessageText": "هل أنت متأكد أنك تريد حذف العلامة \"{0}\"؟",
|
||||
"DeleteNotification": "حذف الإعلام",
|
||||
"DownloadClients": "تحميل العملاء",
|
||||
"DownloadClientStatusCheckAllClientMessage": "جميع عملاء التنزيل غير متاحين بسبب الفشل",
|
||||
"DownloadClientStatusCheckSingleClientMessage": "برامج التنزيل غير متاحة بسبب الإخفاقات: {0}",
|
||||
"DownloadClientStatusAllClientHealthCheckMessage": "جميع عملاء التنزيل غير متاحين بسبب الفشل",
|
||||
"DownloadClientStatusSingleClientHealthCheckMessage": "برامج التنزيل غير متاحة بسبب الإخفاقات: {downloadClientNames}",
|
||||
"Edit": "تعديل",
|
||||
"EditIndexer": "تحرير المفهرس",
|
||||
"Enable": "ممكن",
|
||||
@@ -260,7 +260,7 @@
|
||||
"DownloadClientsLoadError": "تعذر تحميل عملاء التنزيل",
|
||||
"UnableToLoadTags": "تعذر تحميل العلامات",
|
||||
"UnableToLoadUISettings": "تعذر تحميل إعدادات واجهة المستخدم",
|
||||
"UpdateCheckStartupTranslocationMessage": "لا يمكن تثبيت التحديث لأن مجلد بدء التشغيل \"{0}\" موجود في مجلد App Translocation.",
|
||||
"UpdateStartupTranslocationHealthCheckMessage": "لا يمكن تثبيت التحديث لأن مجلد بدء التشغيل \"{startupFolder}\" موجود في مجلد App Translocation.",
|
||||
"Updates": "التحديثات",
|
||||
"Version": "الإصدار",
|
||||
"ApiKey": "مفتاح API",
|
||||
@@ -347,11 +347,13 @@
|
||||
"WhatsNew": "ما هو الجديد؟",
|
||||
"minutes": "الدقائق",
|
||||
"NotificationStatusAllClientHealthCheckMessage": "جميع القوائم غير متاحة بسبب الإخفاقات",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "القوائم غير متاحة بسبب الإخفاقات: {0}",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "القوائم غير متاحة بسبب الإخفاقات: {notificationNames}",
|
||||
"AuthBasic": "أساسي (المتصفح المنبثق)",
|
||||
"AuthForm": "النماذج (صفحة تسجيل الدخول)",
|
||||
"DisabledForLocalAddresses": "معطل بسبب العناوين المحلية",
|
||||
"None": "لا شيء",
|
||||
"ResetAPIKeyMessageText": "هل أنت متأكد أنك تريد إعادة تعيين مفتاح API الخاص بك؟",
|
||||
"RestartProwlarr": "أعد تشغيل {appName}"
|
||||
"RestartProwlarr": "أعد تشغيل {appName}",
|
||||
"CustomFilter": "مرشحات مخصصة",
|
||||
"IndexerHDBitsSettingsMediums": "متوسط"
|
||||
}
|
||||
|
||||
@@ -61,11 +61,11 @@
|
||||
"DeleteBackupMessageText": "Наистина ли искате да изтриете резервното копие '{0}'?",
|
||||
"DeleteTag": "Изтриване на маркера",
|
||||
"DeleteTagMessageText": "Наистина ли искате да изтриете маркера '{0}'?",
|
||||
"IndexerProxyStatusCheckAllClientMessage": "Всички списъци са недостъпни поради неуспехи",
|
||||
"IndexerProxyStatusAllUnavailableHealthCheckMessage": "Всички списъци са недостъпни поради неуспехи",
|
||||
"LastWriteTime": "Време за последно писане",
|
||||
"Columns": "Колони",
|
||||
"EnableRss": "Активиране на RSS",
|
||||
"IndexerProxyStatusCheckSingleClientMessage": "Списъци, недостъпни поради неуспехи: {0}",
|
||||
"IndexerProxyStatusUnavailableHealthCheckMessage": "Списъци, недостъпни поради неуспехи: {indexerProxyNames}",
|
||||
"LogLevel": "Log Level",
|
||||
"MovieIndexScrollTop": "Индекс на филма: Превъртете отгоре",
|
||||
"Queue": "Опашка",
|
||||
@@ -102,7 +102,7 @@
|
||||
"UnableToAddANewIndexerProxyPleaseTryAgain": "Не може да се добави нов индексатор, моля, опитайте отново.",
|
||||
"UnableToAddANewNotificationPleaseTryAgain": "Не може да се добави ново известие, моля, опитайте отново.",
|
||||
"UpdateAutomaticallyHelpText": "Автоматично изтегляне и инсталиране на актуализации. Все още ще можете да инсталирате от System: Updates",
|
||||
"UpdateCheckStartupTranslocationMessage": "Не може да се инсталира актуализация, защото стартовата папка „{0}“ е в папка за преместване на приложения.",
|
||||
"UpdateStartupTranslocationHealthCheckMessage": "Не може да се инсталира актуализация, защото стартовата папка „{startupFolder}“ е в папка за преместване на приложения.",
|
||||
"ReleaseStatus": "Състояние на освобождаване",
|
||||
"UpdateScriptPathHelpText": "Път към персонализиран скрипт, който взема извлечен пакет за актуализация и обработва останалата част от процеса на актуализация",
|
||||
"UseProxy": "Използвай прокси",
|
||||
@@ -200,9 +200,9 @@
|
||||
"PortNumber": "Номер на пристанище",
|
||||
"Proxy": "Прокси",
|
||||
"ProxyBypassFilterHelpText": "Използвайте „,“ като разделител и „*“. като заместващ знак за поддомейни",
|
||||
"ProxyCheckBadRequestMessage": "Неуспешно тестване на прокси. Код на състоянието: {0}",
|
||||
"ProxyCheckFailedToTestMessage": "Неуспешно тестване на прокси: {0}",
|
||||
"ProxyCheckResolveIpMessage": "Неуспешно разрешаване на IP адреса за конфигурирания прокси хост {0}",
|
||||
"ProxyBadRequestHealthCheckMessage": "Неуспешно тестване на прокси. Код на състоянието: {statusCode}",
|
||||
"ProxyFailedToTestHealthCheckMessage": "Неуспешно тестване на прокси: {url}",
|
||||
"ProxyResolveIpHealthCheckMessage": "Неуспешно разрешаване на IP адреса за конфигурирания прокси хост {proxyHostName}",
|
||||
"ProxyPasswordHelpText": "Трябва само да въведете потребителско име и парола, ако е необходимо. В противен случай ги оставете празни.",
|
||||
"ProxyType": "Тип прокси",
|
||||
"ExistingTag": "Съществуващ маркер",
|
||||
@@ -239,8 +239,8 @@
|
||||
"Test": "Тест",
|
||||
"UI": "Потребителски интерфейс",
|
||||
"UILanguage": "Език на потребителския интерфейс",
|
||||
"UpdateCheckStartupNotWritableMessage": "Не може да се инсталира актуализация, тъй като стартовата папка „{0}“ не може да се записва от потребителя „{1}“.",
|
||||
"UpdateCheckUINotWritableMessage": "Не може да се инсталира актуализация, защото папката „{0}“ на потребителския интерфейс не може да се записва от потребителя „{1}“.",
|
||||
"UpdateStartupNotWritableHealthCheckMessage": "Не може да се инсталира актуализация, тъй като стартовата папка „{startupFolder}“ не може да се записва от потребителя „{userName}“.",
|
||||
"UpdateUiNotWritableHealthCheckMessage": "Не може да се инсталира актуализация, защото папката „{uiFolder}“ на потребителския интерфейс не може да се записва от потребителя „{userName}“.",
|
||||
"Enable": "Активиране",
|
||||
"EventType": "Тип на събитието",
|
||||
"Failed": "Се провали",
|
||||
@@ -253,13 +253,13 @@
|
||||
"HideAdvanced": "Скрий Разширено",
|
||||
"Indexer": "Индексатор",
|
||||
"IndexerFlags": "Индексиращи знамена",
|
||||
"IndexerLongTermStatusCheckAllClientMessage": "Всички индексатори са недостъпни поради грешки за повече от 6 часа",
|
||||
"IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Всички индексатори са недостъпни поради грешки за повече от 6 часа",
|
||||
"SSLCertPathHelpText": "Път към pfx файл",
|
||||
"UrlBaseHelpText": "За обратна поддръжка на прокси по подразбиране е празно",
|
||||
"View": "Изглед",
|
||||
"BranchUpdate": "Клон, който да се използва за актуализиране на {appName}",
|
||||
"Indexers": "Индексатори",
|
||||
"IndexerStatusCheckAllClientMessage": "Всички индексатори са недостъпни поради грешки",
|
||||
"IndexerStatusAllUnavailableHealthCheckMessage": "Всички индексатори са недостъпни поради грешки",
|
||||
"Mode": "Режим",
|
||||
"TagsSettingsSummary": "Вижте всички тагове и как се използват. Неизползваните маркери могат да бъдат премахнати",
|
||||
"TestAllClients": "Тествайте всички клиенти",
|
||||
@@ -272,8 +272,8 @@
|
||||
"Updates": "Актуализации",
|
||||
"Uptime": "Време за работа",
|
||||
"DownloadClientSettings": "Изтеглете настройките на клиента",
|
||||
"DownloadClientStatusCheckAllClientMessage": "Всички клиенти за изтегляне са недостъпни поради неуспехи",
|
||||
"DownloadClientStatusCheckSingleClientMessage": "Клиентите за изтегляне са недостъпни поради грешки: {0}",
|
||||
"DownloadClientStatusAllClientHealthCheckMessage": "Всички клиенти за изтегляне са недостъпни поради неуспехи",
|
||||
"DownloadClientStatusSingleClientHealthCheckMessage": "Клиентите за изтегляне са недостъпни поради грешки: {downloadClientNames}",
|
||||
"Edit": "редактиране",
|
||||
"EnableAutomaticSearch": "Активирайте автоматичното търсене",
|
||||
"EnableAutomaticSearchHelpText": "Ще се използва, когато се извършват автоматични търсения чрез потребителския интерфейс или от {appName}",
|
||||
@@ -288,9 +288,9 @@
|
||||
"GeneralSettingsSummary": "Порт, SSL, потребителско име / парола, прокси, анализи и актуализации",
|
||||
"History": "История",
|
||||
"Host": "Водещ",
|
||||
"IndexerLongTermStatusCheckSingleClientMessage": "Индексатори не са налични поради неуспехи за повече от 6 часа: {0}",
|
||||
"IndexerLongTermStatusUnavailableHealthCheckMessage": "Индексатори не са налични поради неуспехи за повече от 6 часа: {indexerNames}",
|
||||
"IndexerPriorityHelpText": "Приоритет на индексатора от 1 (най-висок) до 50 (най-нисък). По подразбиране: 25.",
|
||||
"IndexerStatusCheckSingleClientMessage": "Индексатори не са налични поради грешки: {0}",
|
||||
"IndexerStatusUnavailableHealthCheckMessage": "Индексатори не са налични поради грешки: {indexerNames}",
|
||||
"LaunchBrowserHelpText": " Отворете уеб браузър и отворете началната страница на {appName} при стартиране на приложението.",
|
||||
"ResetAPIKey": "Нулиране на API ключ",
|
||||
"Restart": "Рестартирам",
|
||||
@@ -346,12 +346,14 @@
|
||||
"RecentChanges": "Последни промени",
|
||||
"WhatsNew": "Какво ново?",
|
||||
"minutes": "Минути",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Списъци, недостъпни поради неуспехи: {0}",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Списъци, недостъпни поради неуспехи: {notificationNames}",
|
||||
"NotificationStatusAllClientHealthCheckMessage": "Всички списъци са недостъпни поради неуспехи",
|
||||
"AuthBasic": "Основно (изскачащ прозорец на браузъра)",
|
||||
"AuthForm": "Формуляри (Страница за вход)",
|
||||
"DisabledForLocalAddresses": "Забранено за местни адреси",
|
||||
"None": "Нито един",
|
||||
"ResetAPIKeyMessageText": "Наистина ли искате да нулирате своя API ключ?",
|
||||
"RestartProwlarr": "Рестартирайте {appName}"
|
||||
"RestartProwlarr": "Рестартирайте {appName}",
|
||||
"IndexerHDBitsSettingsMediums": "Среден",
|
||||
"CustomFilter": "Персонализирани филтри"
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
"Retention": "Retenció",
|
||||
"Title": "Títol",
|
||||
"DeleteNotification": "Suprimeix la notificació",
|
||||
"ProxyCheckBadRequestMessage": "No s'ha pogut provar el servidor intermediari. Codi d'estat: {0}",
|
||||
"ProxyBadRequestHealthCheckMessage": "No s'ha pogut provar el servidor intermediari. Codi d'estat: {statusCode}",
|
||||
"Reddit": "Reddit",
|
||||
"System": "Sistema",
|
||||
"Username": "Nom d'usuari",
|
||||
@@ -128,8 +128,8 @@
|
||||
"Filters": "Filtres",
|
||||
"FocusSearchBox": "Posa el focus a la caixa de cerca",
|
||||
"Grabbed": "Capturat",
|
||||
"IndexerStatusCheckAllClientMessage": "Tots els indexadors no estan disponibles a causa d'errors",
|
||||
"IndexerStatusCheckSingleClientMessage": "Els indexadors no estan disponibles a causa d'errors: {0}",
|
||||
"IndexerStatusAllUnavailableHealthCheckMessage": "Tots els indexadors no estan disponibles a causa d'errors",
|
||||
"IndexerStatusUnavailableHealthCheckMessage": "Els indexadors no estan disponibles a causa d'errors: {indexerNames}",
|
||||
"MovieIndexScrollTop": "Índex de pel·lícules: Desplaçament superior",
|
||||
"NotificationTriggers": "Activadors de notificacions",
|
||||
"NotificationTriggersHelpText": "Seleccioneu quins esdeveniments haurien d'activar aquesta notificació",
|
||||
@@ -175,7 +175,7 @@
|
||||
"DeleteTagMessageText": "Esteu segur que voleu suprimir l'etiqueta '{label}'?",
|
||||
"Details": "Detalls",
|
||||
"Disabled": "Desactivat",
|
||||
"DownloadClientStatusCheckAllClientMessage": "Tots els clients de descàrrega no estan disponibles a causa d'errors",
|
||||
"DownloadClientStatusAllClientHealthCheckMessage": "Tots els clients de descàrrega no estan disponibles a causa d'errors",
|
||||
"Edit": "Edita",
|
||||
"EnableInteractiveSearchHelpText": "S'utilitzarà quan s'utilitzi la cerca interactiva",
|
||||
"EnableInteractiveSearch": "Activa la cerca interactiva",
|
||||
@@ -198,8 +198,8 @@
|
||||
"OnApplicationUpdateHelpText": "A l'actualitzar de l'aplicació",
|
||||
"OnGrab": "Al capturar",
|
||||
"PackageVersion": "Versió del paquet",
|
||||
"ProxyCheckFailedToTestMessage": "No s'ha pogut provar el servidor intermediari: {0}",
|
||||
"ProxyCheckResolveIpMessage": "No s'ha pogut resoldre l'adreça IP de l'amfitrió intermediari configurat {0}",
|
||||
"ProxyFailedToTestHealthCheckMessage": "No s'ha pogut provar el servidor intermediari: {url}",
|
||||
"ProxyResolveIpHealthCheckMessage": "No s'ha pogut resoldre l'adreça IP de l'amfitrió intermediari configurat {proxyHostName}",
|
||||
"Queued": "En cua",
|
||||
"ReadTheWikiForMoreInformation": "Llegiu el Wiki per a més informació",
|
||||
"RestartRequiredHelpTextWarning": "Cal reiniciar perquè tingui efecte",
|
||||
@@ -235,13 +235,13 @@
|
||||
"Enable": "Activa",
|
||||
"IndexerFlags": "Indicadors de l'indexador",
|
||||
"UnableToLoadNotifications": "No es poden carregar les notificacions",
|
||||
"IndexerLongTermStatusCheckAllClientMessage": "Tots els indexadors no estan disponibles a causa d'errors durant més de 6 hores",
|
||||
"IndexerLongTermStatusCheckSingleClientMessage": "Els indexadors no estan disponibles a causa d'errors durant més de 6 hores: {0}",
|
||||
"IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Tots els indexadors no estan disponibles a causa d'errors durant més de 6 hores",
|
||||
"IndexerLongTermStatusUnavailableHealthCheckMessage": "Els indexadors no estan disponibles a causa d'errors durant més de 6 hores: {indexerNames}",
|
||||
"IndexerPriority": "Prioritat de l'indexador",
|
||||
"UnsavedChanges": "Canvis no desats",
|
||||
"UpdateAutomaticallyHelpText": "Baixeu i instal·leu les actualitzacions automàticament. Encara podreu instal·lar des de Sistema: Actualitzacions",
|
||||
"UpdateCheckStartupTranslocationMessage": "No es pot instal·lar l'actualització perquè la carpeta d'inici \"{0}\" es troba en una carpeta de translocació d'aplicacions.",
|
||||
"UpdateCheckUINotWritableMessage": "No es pot instal·lar l'actualització perquè l'usuari '{1}' no pot escriure la carpeta de la IU '{0}'.",
|
||||
"UpdateStartupTranslocationHealthCheckMessage": "No es pot instal·lar l'actualització perquè la carpeta d'inici \"{startupFolder}\" es troba en una carpeta de translocació d'aplicacions.",
|
||||
"UpdateUiNotWritableHealthCheckMessage": "No es pot instal·lar l'actualització perquè l'usuari '{userName}' no pot escriure la carpeta de la IU '{uiFolder}'.",
|
||||
"UpdateScriptPathHelpText": "Camí a un script personalitzat que pren un paquet d'actualització i gestiona la resta del procés d'actualització",
|
||||
"Uptime": "Temps de funcionament",
|
||||
"Info": "Informació",
|
||||
@@ -266,7 +266,7 @@
|
||||
"Discord": "Discord",
|
||||
"Docker": "Docker",
|
||||
"Donations": "Donacions",
|
||||
"DownloadClientStatusCheckSingleClientMessage": "Baixa els clients no disponibles a causa d'errors: {0}",
|
||||
"DownloadClientStatusSingleClientHealthCheckMessage": "Baixa els clients no disponibles a causa d'errors: {downloadClientNames}",
|
||||
"HealthNoIssues": "No hi ha cap problema amb la configuració",
|
||||
"HideAdvanced": "Amaga avançat",
|
||||
"History": "Història",
|
||||
@@ -294,7 +294,7 @@
|
||||
"UnableToAddANewNotificationPleaseTryAgain": "No es pot afegir una notificació nova, torneu-ho a provar.",
|
||||
"UnableToLoadGeneralSettings": "No es pot carregar la configuració general",
|
||||
"UnableToLoadHistory": "No es pot carregar l'historial",
|
||||
"UpdateCheckStartupNotWritableMessage": "No es pot instal·lar l'actualització perquè l'usuari \"{1}\" no pot escriure la carpeta d'inici \"{0}\".",
|
||||
"UpdateStartupNotWritableHealthCheckMessage": "No es pot instal·lar l'actualització perquè l'usuari \"{userName}\" no pot escriure la carpeta d'inici \"{startupFolder}\".",
|
||||
"URLBase": "Base URL",
|
||||
"Usenet": "Usenet",
|
||||
"View": "Visualitza",
|
||||
@@ -333,8 +333,8 @@
|
||||
"UnableToAddANewIndexerProxyPleaseTryAgain": "No es pot afegir un indexador nou, torneu-ho a provar.",
|
||||
"UpdateMechanismHelpText": "Utilitzeu l'actualitzador integrat de {appName} o un script",
|
||||
"UserAgentProvidedByTheAppThatCalledTheAPI": "Agent d'usuari proporcionat per l'aplicació per fer peticions a l'API",
|
||||
"IndexerProxyStatusCheckAllClientMessage": "Tots els indexadors no estan disponibles a causa d'errors",
|
||||
"IndexerProxyStatusCheckSingleClientMessage": "Els indexadors no estan disponibles a causa d'errors: {0}",
|
||||
"IndexerProxyStatusAllUnavailableHealthCheckMessage": "Tots els indexadors no estan disponibles a causa d'errors",
|
||||
"IndexerProxyStatusUnavailableHealthCheckMessage": "Els indexadors no estan disponibles a causa d'errors: {indexerProxyNames}",
|
||||
"LaunchBrowserHelpText": " Obriu un navegador web i navegueu a la pàgina d'inici de {appName} a l'inici de l'aplicació.",
|
||||
"Link": "Enllaços",
|
||||
"UILanguageHelpText": "Idioma que utilitzarà {appName} per a la interfície d'usuari",
|
||||
@@ -362,14 +362,14 @@
|
||||
"Theme": "Tema",
|
||||
"Track": "Traça",
|
||||
"Year": "Any",
|
||||
"UpdateAvailable": "Nova actualització disponible",
|
||||
"UpdateAvailableHealthCheckMessage": "Nova actualització disponible",
|
||||
"ConnectionLostReconnect": "{appName} intentarà connectar-se automàticament, o podeu fer clic a recarregar.",
|
||||
"ConnectionLostToBackend": "{appName} ha perdut la connexió amb el backend i s'haurà de tornar a carregar per a restaurar la funcionalitat.",
|
||||
"RecentChanges": "Canvis recents",
|
||||
"WhatsNew": "Que hi ha de nou?",
|
||||
"minutes": "Minuts",
|
||||
"DeleteAppProfileMessageText": "Esteu segur que voleu suprimir el perfil de qualitat {0}",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Llistes no disponibles a causa d'errors: {0}",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Llistes no disponibles a causa d'errors: {notificationNames}",
|
||||
"AddConnection": "Afegeix una connexió",
|
||||
"NotificationStatusAllClientHealthCheckMessage": "Totes les notificacions no estan disponibles a causa d'errors",
|
||||
"AuthBasic": "Basic (finestra emergent del navegador)",
|
||||
@@ -394,7 +394,7 @@
|
||||
"EditIndexerImplementation": "Edita l'indexador - {implementationName}",
|
||||
"EditSelectedDownloadClients": "Editeu els clients de descàrrega seleccionats",
|
||||
"EditSelectedIndexers": "Edita els indexadors seleccionats",
|
||||
"IndexerDownloadClientHealthCheckMessage": "Indexadors amb clients de baixada no vàlids: {0}.",
|
||||
"IndexerDownloadClientHealthCheckMessage": "Indexadors amb clients de baixada no vàlids: {indexerNames}.",
|
||||
"AddCustomFilter": "Afegeix un filtre personalitzat",
|
||||
"AddDownloadClientImplementation": "Afegeix un client de descàrrega - {implementationName}",
|
||||
"AddIndexerImplementation": "Afegeix un indexador - {implementationName}",
|
||||
@@ -431,5 +431,17 @@
|
||||
"Id": "ID",
|
||||
"Author": "Autor",
|
||||
"ManageClients": "Gestiona els clients",
|
||||
"CountApplicationsSelected": "{count} Col·lecció(ns) seleccionades"
|
||||
"CountApplicationsSelected": "{count} Col·lecció(ns) seleccionades",
|
||||
"DownloadClientAriaSettingsDirectoryHelpText": "Ubicació opcional per a les baixades, deixeu-lo en blanc per utilitzar la ubicació predeterminada d'Aria2",
|
||||
"Donate": "Dona",
|
||||
"BlackholeFolderHelpText": "Carpeta on {appName} emmagatzemarà els fitxers {extension}",
|
||||
"Destination": "Destinació",
|
||||
"Directory": "Directori",
|
||||
"DownloadClientDelugeSettingsUrlBaseHelpText": "Afegeix un prefix a l'url json del Deluge, vegeu {url}",
|
||||
"CustomFilter": "Filtres personalitzats",
|
||||
"IndexerHDBitsSettingsCodecs": "Còdec",
|
||||
"DownloadClientSettingsUrlBaseHelpText": "Afegeix un prefix a l'URL {connectionName}, com ara {url}",
|
||||
"DownloadClientTransmissionSettingsDirectoryHelpText": "Ubicació opcional per a les baixades, deixeu-lo en blanc per utilitzar la ubicació predeterminada d'Aria2",
|
||||
"DownloadClientRTorrentSettingsDirectoryHelpText": "Ubicació opcional de les baixades completades, deixeu-lo en blanc per utilitzar la ubicació predeterminada de Deluge",
|
||||
"IndexerHDBitsSettingsMediums": "Suport"
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"ProxyType": "Typ serveru proxy",
|
||||
"Reddit": "Reddit",
|
||||
"ErrorLoadingContents": "Chyba při načítání obsahu",
|
||||
"IndexerLongTermStatusCheckAllClientMessage": "Všechny indexery nejsou k dispozici z důvodu selhání po dobu delší než 6 hodin",
|
||||
"IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Všechny indexery nejsou k dispozici z důvodu selhání po dobu delší než 6 hodin",
|
||||
"RemovedFromTaskQueue": "Odebráno z fronty úkolů",
|
||||
"ResetAPIKey": "Resetovat klíč API",
|
||||
"SSLCertPassword": "Heslo SSL Cert",
|
||||
@@ -41,8 +41,8 @@
|
||||
"Docker": "Přístavní dělník",
|
||||
"Donations": "Dary",
|
||||
"DownloadClientSettings": "Stáhněte si nastavení klienta",
|
||||
"DownloadClientStatusCheckAllClientMessage": "Všichni klienti pro stahování nejsou kvůli chybám k dispozici",
|
||||
"DownloadClientStatusCheckSingleClientMessage": "Stahování klientů není k dispozici z důvodu selhání: {0}",
|
||||
"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}",
|
||||
"Folder": "Složka",
|
||||
"Grabs": "Urvat",
|
||||
"HealthNoIssues": "Žádné problémy s vaší konfigurací",
|
||||
@@ -55,7 +55,7 @@
|
||||
"IndexerPriority": "Priorita indexování",
|
||||
"IndexerPriorityHelpText": "Priorita indexování od 1 (nejvyšší) do 50 (nejnižší). Výchozí: 25.",
|
||||
"Indexers": "Indexery",
|
||||
"IndexerStatusCheckAllClientMessage": "Všechny indexery nejsou k dispozici z důvodu selhání",
|
||||
"IndexerStatusAllUnavailableHealthCheckMessage": "Všechny indexery nejsou k dispozici z důvodu selhání",
|
||||
"LastWriteTime": "Čas posledního zápisu",
|
||||
"Level": "Úroveň",
|
||||
"LogLevel": "Úroveň protokolu",
|
||||
@@ -67,7 +67,7 @@
|
||||
"Ok": "OK",
|
||||
"SendAnonymousUsageData": "Odesílejte anonymní údaje o používání",
|
||||
"UnselectAll": "Odznačit vše",
|
||||
"UpdateCheckStartupNotWritableMessage": "Aktualizaci nelze nainstalovat, protože spouštěcí složku „{0}“ nelze zapisovat uživatelem „{1}“.",
|
||||
"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.",
|
||||
"ApiKey": "Klíč API",
|
||||
@@ -105,7 +105,7 @@
|
||||
"Tasks": "Úkoly",
|
||||
"Test": "Test",
|
||||
"UnableToLoadTags": "Značky nelze načíst",
|
||||
"IndexerProxyStatusCheckAllClientMessage": "Všechny indexery nejsou k dispozici z důvodu selhání",
|
||||
"IndexerProxyStatusAllUnavailableHealthCheckMessage": "Všechny indexery nejsou k dispozici z důvodu selhání",
|
||||
"ApplyTags": "Použít značky",
|
||||
"MoreInfo": "Více informací",
|
||||
"System": "Systém",
|
||||
@@ -124,9 +124,9 @@
|
||||
"Grabbed": "Popadl",
|
||||
"Health": "Zdraví",
|
||||
"LogLevelTraceHelpTextWarning": "Trasování protokolování by mělo být povoleno pouze dočasně",
|
||||
"ProxyCheckBadRequestMessage": "Nepodařilo se otestovat proxy. StatusCode: {0}",
|
||||
"ProxyCheckFailedToTestMessage": "Nepodařilo se otestovat proxy: {0}",
|
||||
"ProxyCheckResolveIpMessage": "Nepodařilo se vyřešit adresu IP konfigurovaného hostitele proxy {0}",
|
||||
"ProxyBadRequestHealthCheckMessage": "Nepodařilo se otestovat proxy. StatusCode: {statusCode}",
|
||||
"ProxyFailedToTestHealthCheckMessage": "Nepodařilo se otestovat proxy: {url}",
|
||||
"ProxyResolveIpHealthCheckMessage": "Nepodařilo se vyřešit adresu IP konfigurovaného hostitele proxy {proxyHostName}",
|
||||
"ProxyPasswordHelpText": "Musíte pouze zadat uživatelské jméno a heslo, pokud je požadováno. Jinak je nechte prázdné.",
|
||||
"ProxyUsernameHelpText": "Musíte pouze zadat uživatelské jméno a heslo, pokud je požadováno. Jinak je nechte prázdné.",
|
||||
"Queue": "Fronta",
|
||||
@@ -161,8 +161,8 @@
|
||||
"UnableToAddANewIndexerPleaseTryAgain": "Nelze přidat nový indexer, zkuste to znovu.",
|
||||
"UnableToAddANewIndexerProxyPleaseTryAgain": "Nelze přidat nový indexer, zkuste to znovu.",
|
||||
"UnableToLoadNotifications": "Nelze načíst oznámení",
|
||||
"UpdateCheckStartupTranslocationMessage": "Aktualizaci nelze nainstalovat, protože spouštěcí složka „{0}“ je ve složce Translocation aplikace.",
|
||||
"UpdateCheckUINotWritableMessage": "Aktualizaci nelze nainstalovat, protože uživatelská složka „{0}“ není zapisovatelná uživatelem „{1}“.",
|
||||
"UpdateStartupTranslocationHealthCheckMessage": "Aktualizaci nelze nainstalovat, protože spouštěcí složka „{startupFolder}“ je ve složce Translocation aplikace.",
|
||||
"UpdateUiNotWritableHealthCheckMessage": "Aktualizaci nelze nainstalovat, protože uživatelská složka „{uiFolder}“ není zapisovatelná uživatelem „{userName}“.",
|
||||
"UpdateMechanismHelpText": "Použijte vestavěný aktualizátor {appName} nebo skript",
|
||||
"UpdateScriptPathHelpText": "Cesta k vlastnímu skriptu, který přebírá extrahovaný balíček aktualizace a zpracovává zbytek procesu aktualizace",
|
||||
"Uptime": "Provozuschopnost",
|
||||
@@ -183,7 +183,7 @@
|
||||
"BypassProxyForLocalAddresses": "Obcházení proxy serveru pro místní adresy",
|
||||
"DeleteIndexerProxyMessageText": "Opravdu chcete smazat značku „{0}“?",
|
||||
"DeleteTag": "Smazat značku",
|
||||
"IndexerProxyStatusCheckSingleClientMessage": "Indexery nedostupné z důvodu selhání: {0}",
|
||||
"IndexerProxyStatusUnavailableHealthCheckMessage": "Indexery nedostupné z důvodu selhání: {indexerProxyNames}",
|
||||
"Name": "název",
|
||||
"New": "Nový",
|
||||
"Protocol": "Protokol",
|
||||
@@ -265,8 +265,8 @@
|
||||
"Exception": "Výjimka",
|
||||
"ExistingTag": "Stávající značka",
|
||||
"IllRestartLater": "Restartuji později",
|
||||
"IndexerLongTermStatusCheckSingleClientMessage": "Indexery nedostupné z důvodu selhání po dobu delší než 6 hodin: {0}",
|
||||
"IndexerStatusCheckSingleClientMessage": "Indexery nedostupné z důvodu selhání: {0}",
|
||||
"IndexerLongTermStatusUnavailableHealthCheckMessage": "Indexery nedostupné z důvodu selhání po dobu delší než 6 hodin: {indexerNames}",
|
||||
"IndexerStatusUnavailableHealthCheckMessage": "Indexery nedostupné z důvodu selhání: {indexerNames}",
|
||||
"SettingsTimeFormat": "Časový formát",
|
||||
"ShowAdvanced": "Zobrazit pokročilé",
|
||||
"ShowSearch": "Zobrazit vyhledávání",
|
||||
@@ -353,7 +353,7 @@
|
||||
"ApplicationURL": "URL aplikace",
|
||||
"ApplicationUrlHelpText": "Externí adresa URL této aplikace včetně http(s)://, portu a základní adresy URL",
|
||||
"ApplyChanges": "Použít změny",
|
||||
"ApiKeyValidationHealthCheckMessage": "Aktualizujte svůj klíč API tak, aby měl alespoň {0} znaků. Můžete to provést prostřednictvím nastavení nebo konfiguračního souboru",
|
||||
"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í",
|
||||
@@ -371,7 +371,7 @@
|
||||
"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í: {0}",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Seznamy nejsou k dispozici 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í",
|
||||
@@ -408,5 +408,9 @@
|
||||
"AuthenticationRequiredPasswordConfirmationHelpTextWarning": "Potvrďte nové heslo",
|
||||
"days": "dnů",
|
||||
"Id": "ID",
|
||||
"CountApplicationsSelected": "Vybráno {0} kolekcí"
|
||||
"CountApplicationsSelected": "Vybráno {0} kolekcí",
|
||||
"IndexerHDBitsSettingsCodecs": "Kodek",
|
||||
"IndexerHDBitsSettingsMediums": "Střední",
|
||||
"Directory": "Adresář",
|
||||
"CustomFilter": "Vlastní filtry"
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"Language": "Sprog",
|
||||
"KeyboardShortcuts": "Keyboard Genveje",
|
||||
"Info": "Information",
|
||||
"IndexerStatusCheckSingleClientMessage": "Indexere utilgængelige på grund af fejl: {0}",
|
||||
"IndexerStatusCheckAllClientMessage": "Alle indeksører er utilgængelige på grund af fejl",
|
||||
"IndexerStatusUnavailableHealthCheckMessage": "Indexere utilgængelige på grund af fejl: {indexerNames}",
|
||||
"IndexerStatusAllUnavailableHealthCheckMessage": "Alle indeksører er utilgængelige på grund af fejl",
|
||||
"Indexers": "Indexere",
|
||||
"History": "Historie",
|
||||
"HideAdvanced": "Gemt Avancerede",
|
||||
@@ -21,8 +21,8 @@
|
||||
"Events": "Begivenheder",
|
||||
"Error": "Fejl",
|
||||
"Edit": "Rediger",
|
||||
"DownloadClientStatusCheckSingleClientMessage": "Download klienter er ikke tilgængelige på grund af fejl: {0}",
|
||||
"DownloadClientStatusCheckAllClientMessage": "Alle download klienter er utilgængelige på grund af fejl",
|
||||
"DownloadClientStatusSingleClientHealthCheckMessage": "Download klienter er ikke tilgængelige på grund af fejl: {downloadClientNames}",
|
||||
"DownloadClientStatusAllClientHealthCheckMessage": "Alle download klienter er utilgængelige på grund af fejl",
|
||||
"DownloadClientsSettingsSummary": "Indstilling af downloadklienter til brug for {appName}s søgefunktion",
|
||||
"DownloadClients": "Download Klienter",
|
||||
"DownloadClient": "Download Klient",
|
||||
@@ -135,12 +135,12 @@
|
||||
"IllRestartLater": "Jeg genstarter senere",
|
||||
"IncludeHealthWarningsHelpText": "Inkluder sundhedsadvarsler",
|
||||
"Indexer": "Indekser",
|
||||
"IndexerLongTermStatusCheckAllClientMessage": "Alle indeksatorer er ikke tilgængelige på grund af fejl i mere end 6 timer",
|
||||
"IndexerLongTermStatusCheckSingleClientMessage": "Indeksatorer er ikke tilgængelige på grund af fejl i mere end 6 timer: {0}",
|
||||
"IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Alle indeksatorer er ikke tilgængelige på grund af fejl i mere end 6 timer",
|
||||
"IndexerLongTermStatusUnavailableHealthCheckMessage": "Indeksatorer er ikke tilgængelige på grund af fejl i mere end 6 timer: {indexerNames}",
|
||||
"IndexerPriority": "Indekseringsprioritet",
|
||||
"IndexerPriorityHelpText": "Indekseringsprioritet fra 1 (højest) til 50 (lavest). Standard: 25.",
|
||||
"IndexerProxyStatusCheckAllClientMessage": "Alle indexere er utilgængelige på grund af fejl",
|
||||
"IndexerProxyStatusCheckSingleClientMessage": "Indexere utilgængelige på grund af fejl: {0}",
|
||||
"IndexerProxyStatusAllUnavailableHealthCheckMessage": "Alle indexere er utilgængelige på grund af fejl",
|
||||
"IndexerProxyStatusUnavailableHealthCheckMessage": "Indexere utilgængelige på grund af fejl: {indexerProxyNames}",
|
||||
"LaunchBrowserHelpText": " Åbn en webbrowser, og naviger til {appName}-hjemmesiden ved start af appen.",
|
||||
"Logging": "Logning",
|
||||
"LogLevel": "Logniveau",
|
||||
@@ -173,9 +173,9 @@
|
||||
"Priority": "Prioritet",
|
||||
"Protocol": "Protokol",
|
||||
"ProxyBypassFilterHelpText": "Brug ',' som en separator og '*.' som et jokertegn for underdomæner",
|
||||
"ProxyCheckBadRequestMessage": "Kunne ikke teste proxy. Statuskode: {0}",
|
||||
"ProxyCheckFailedToTestMessage": "Kunne ikke teste proxy: {0}",
|
||||
"ProxyCheckResolveIpMessage": "Mislykkedes at løse IP-adressen til den konfigurerede proxyhost {0}",
|
||||
"ProxyBadRequestHealthCheckMessage": "Kunne ikke teste proxy. Statuskode: {statusCode}",
|
||||
"ProxyFailedToTestHealthCheckMessage": "Kunne ikke teste proxy: {url}",
|
||||
"ProxyResolveIpHealthCheckMessage": "Mislykkedes at løse IP-adressen til den konfigurerede proxyhost {proxyHostName}",
|
||||
"ProxyPasswordHelpText": "Du skal kun indtaste et brugernavn og en adgangskode, hvis der kræves et. Lad dem være tomme ellers.",
|
||||
"ProxyUsernameHelpText": "Du skal kun indtaste et brugernavn og en adgangskode, hvis der kræves et. Lad dem være tomme ellers.",
|
||||
"ReadTheWikiForMoreInformation": "Læs Wiki for mere information",
|
||||
@@ -239,8 +239,8 @@
|
||||
"UnableToLoadUISettings": "UI-indstillingerne kunne ikke indlæses",
|
||||
"UnselectAll": "Fravælg alle",
|
||||
"UpdateAutomaticallyHelpText": "Download og installer opdateringer automatisk. Du kan stadig installere fra System: Updates",
|
||||
"UpdateCheckStartupNotWritableMessage": "Kan ikke installere opdatering, fordi startmappen '{0}' ikke kan skrives af brugeren '{1}'.",
|
||||
"UpdateCheckUINotWritableMessage": "Kan ikke installere opdatering, fordi brugergrænsefladen \"{0}\" ikke kan skrives af brugeren \"{1}\".",
|
||||
"UpdateStartupNotWritableHealthCheckMessage": "Kan ikke installere opdatering, fordi startmappen '{startupFolder}' ikke kan skrives af brugeren '{userName}'.",
|
||||
"UpdateUiNotWritableHealthCheckMessage": "Kan ikke installere opdatering, fordi brugergrænsefladen \"{uiFolder}\" ikke kan skrives af brugeren \"{userName}\".",
|
||||
"UpdateScriptPathHelpText": "Sti til et brugerdefineret script, der tager en udpakket opdateringspakke og håndterer resten af opdateringsprocessen",
|
||||
"Uptime": "Oppetid",
|
||||
"URLBase": "URL-base",
|
||||
@@ -265,7 +265,7 @@
|
||||
"StartupDirectory": "Startmappe",
|
||||
"Status": "Status",
|
||||
"DownloadClientsLoadError": "Kunne ikke indlæse downloadklienter",
|
||||
"UpdateCheckStartupTranslocationMessage": "Kan ikke installere opdatering, fordi startmappen '{0}' er i en App Translocation-mappe.",
|
||||
"UpdateStartupTranslocationHealthCheckMessage": "Kan ikke installere opdatering, fordi startmappen '{startupFolder}' er i en App Translocation-mappe.",
|
||||
"UpdateMechanismHelpText": "Brug den indbyggede opdateringsfunktion eller et script",
|
||||
"View": "Udsigt",
|
||||
"Warn": "Advare",
|
||||
@@ -363,7 +363,7 @@
|
||||
"ConnectionLostReconnect": "Radarr vil prøve at tilslutte automatisk, eller 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: {0}",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Lister utilgængelige på grund af fejl: {notificationNames}",
|
||||
"AuthForm": "Formularer (login-side)",
|
||||
"DisabledForLocalAddresses": "Deaktiveret for lokale adresser",
|
||||
"ResetAPIKeyMessageText": "Er du sikker på, at du vil nulstille din API-nøgle?",
|
||||
@@ -377,5 +377,20 @@
|
||||
"AddApplicationImplementation": "Tilføj forbindelse - {implementationName}",
|
||||
"AddIndexerImplementation": "Tilføj betingelse - {implementationName}",
|
||||
"ApplyChanges": "Anvend ændringer",
|
||||
"AddDownloadClientImplementation": "Tilføj downloadklient - {implementationName}"
|
||||
"AddDownloadClientImplementation": "Tilføj downloadklient - {implementationName}",
|
||||
"Album": "album",
|
||||
"Theme": "Tema",
|
||||
"Categories": "Kategorier",
|
||||
"Application": "Applikationer",
|
||||
"EditIndexerProxyImplementation": "Tilføj betingelse - {implementationName}",
|
||||
"Publisher": "Udgiver",
|
||||
"IndexerHDBitsSettingsCodecs": "codec",
|
||||
"Applications": "Applikationer",
|
||||
"AddIndexerProxyImplementation": "Tilføj betingelse - {implementationName}",
|
||||
"EditIndexerImplementation": "Tilføj betingelse - {implementationName}",
|
||||
"Directory": "Mappe",
|
||||
"EditApplicationImplementation": "Tilføj forbindelse - {implementationName}",
|
||||
"EditDownloadClientImplementation": "Tilføj downloadklient - {implementationName}",
|
||||
"IndexerHDBitsSettingsMediums": "Medium",
|
||||
"CustomFilter": "Bruger Tilpassede Filtere"
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"Analytics": "Analysen",
|
||||
"AnalyticsEnabledHelpText": "Senden Sie anonyme Nutzungs- und Fehlerinformationen an die Server von {appName}. Dazu gehören Informationen zu Ihrem Browser, welche {appName}-WebUI-Seiten Sie verwenden, Fehlerberichte sowie Betriebssystem- und Laufzeitversion. Wir werden diese Informationen verwenden, um Funktionen und Fehlerbehebungen zu priorisieren.",
|
||||
"ApiKey": "API-Schlüssel",
|
||||
"ApiKeyValidationHealthCheckMessage": "Bitte den API Schlüssel korrigieren, dieser muss mindestens {0} Zeichen lang sein. Die Änderung kann über die Einstellungen oder die Konfigurationsdatei erfolgen",
|
||||
"ApiKeyValidationHealthCheckMessage": "Bitte den API Schlüssel korrigieren, dieser muss mindestens {length} Zeichen lang sein. Die Änderung kann über die Einstellungen oder die Konfigurationsdatei erfolgen",
|
||||
"AppDataDirectory": "AppData-Verzeichnis",
|
||||
"AppDataLocationHealthCheckMessage": "Ein Update ist nicht möglich, um das Löschen von AppData beim Update zu verhindern",
|
||||
"AppProfileInUse": "App-Profil im Einsatz",
|
||||
@@ -107,8 +107,8 @@
|
||||
"Donations": "Spenden",
|
||||
"DownloadClient": "Download Client",
|
||||
"DownloadClientSettings": "Downloader Einstellungen",
|
||||
"DownloadClientStatusCheckAllClientMessage": "Alle Download Clients sind aufgrund von Fehlern nicht verfügbar",
|
||||
"DownloadClientStatusCheckSingleClientMessage": "Download Clients aufgrund von Fehlern nicht verfügbar: {0}",
|
||||
"DownloadClientStatusAllClientHealthCheckMessage": "Alle Download Clients sind aufgrund von Fehlern nicht verfügbar",
|
||||
"DownloadClientStatusSingleClientHealthCheckMessage": "Download Clients aufgrund von Fehlern nicht verfügbar: {downloadClientNames}",
|
||||
"DownloadClients": "Downloader",
|
||||
"DownloadClientsSettingsSummary": "Download der Client-Konfigurationen für die Integration in die {appName} UI-Suche",
|
||||
"Duration": "Dauer",
|
||||
@@ -176,8 +176,8 @@
|
||||
"IndexerFlags": "Indexer-Flags",
|
||||
"IndexerHealthCheckNoIndexers": "Keine Indexer aktiviert, {appName} wird keine Suchergebnisse zurückgeben",
|
||||
"IndexerInfo": "Indexer-Info",
|
||||
"IndexerLongTermStatusCheckAllClientMessage": "Alle Indexer sind wegen über 6 Stunden langen bestehender Fehler nicht verfügbar",
|
||||
"IndexerLongTermStatusCheckSingleClientMessage": "Indexer wegen über 6 Stunden langen bestehenden Fehlern nicht verfügbar: {0}",
|
||||
"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}",
|
||||
"IndexerName": "Indexer-Name",
|
||||
"IndexerNoDefCheckMessage": "Indexer haben keine Definition und werden nicht funktionieren: {0}. 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",
|
||||
@@ -185,17 +185,17 @@
|
||||
"IndexerPriorityHelpText": "Indexer Priorität von 1 (höchste) bis 50 (niedrigste). Standard: 25.",
|
||||
"IndexerProxies": "Indexer-Proxies",
|
||||
"IndexerProxy": "Indexer-Proxy",
|
||||
"IndexerProxyStatusCheckAllClientMessage": "Alle Indexer sind aufgrund von Fehlern nicht verfügbar",
|
||||
"IndexerProxyStatusCheckSingleClientMessage": "Listen aufgrund von Fehlern nicht verfügbar: {0}",
|
||||
"IndexerProxyStatusAllUnavailableHealthCheckMessage": "Alle Indexer sind aufgrund von Fehlern nicht verfügbar",
|
||||
"IndexerProxyStatusUnavailableHealthCheckMessage": "Listen aufgrund von Fehlern nicht verfügbar: {indexerProxyNames}",
|
||||
"IndexerQuery": "Indexer Anfrage",
|
||||
"IndexerRss": "Indexer RSS",
|
||||
"IndexerSettingsSummary": "Konfiguration verschiedener globaler Indexer Einstellungen, einschließlich Proxies.",
|
||||
"IndexerSite": "Indexer-Seite",
|
||||
"IndexerStatusCheckAllClientMessage": "Alle Indexer sind aufgrund von Fehlern nicht verfügbar",
|
||||
"IndexerStatusCheckSingleClientMessage": "Indexer aufgrund von Fehlern nicht verfügbar: {0}",
|
||||
"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.",
|
||||
"IndexerVipCheckExpiredClientMessage": "Die VIP Indexer Vorteile sind abgelaufen: {0}",
|
||||
"IndexerVipCheckExpiringClientMessage": "Die Indexer VIP Vorteile verfallen bald: {0}",
|
||||
"IndexerVipExpiredHealthCheckMessage": "Die VIP Indexer Vorteile sind abgelaufen: {indexerNames}",
|
||||
"IndexerVipExpiringHealthCheckMessage": "Die Indexer VIP Vorteile verfallen bald: {indexerNames}",
|
||||
"Indexers": "Indexer",
|
||||
"Info": "Info",
|
||||
"InstanceName": "Instanzname",
|
||||
@@ -281,9 +281,9 @@
|
||||
"Proxies": "Proxies",
|
||||
"Proxy": "Proxy",
|
||||
"ProxyBypassFilterHelpText": "Verwenden Sie ',' als Trennzeichen und '*.' als Wildcard für Subdomains",
|
||||
"ProxyCheckBadRequestMessage": "Proxy konnte nicht getestet werden. StatusCode: {0}",
|
||||
"ProxyCheckFailedToTestMessage": "Proxy konnte nicht getestet werden: {0}",
|
||||
"ProxyCheckResolveIpMessage": "Fehler beim Auflösen der IP-Adresse für den konfigurierten Proxy-Host {0}",
|
||||
"ProxyBadRequestHealthCheckMessage": "Proxy konnte nicht getestet werden. StatusCode: {statusCode}",
|
||||
"ProxyFailedToTestHealthCheckMessage": "Proxy konnte nicht getestet werden: {url}",
|
||||
"ProxyResolveIpHealthCheckMessage": "Fehler beim Auflösen der IP-Adresse für den konfigurierten Proxy-Host {proxyHostName}",
|
||||
"ProxyPasswordHelpText": "Sie müssen nur einen Benutzernamen und ein Passwort eingeben, wenn dies erforderlich ist. Andernfalls lassen Sie sie leer.",
|
||||
"ProxyType": "Proxy-Typ",
|
||||
"ProxyUsernameHelpText": "Sie müssen nur einen Benutzernamen und ein Passwort eingeben, wenn dies erforderlich ist. Andernfalls lassen Sie sie leer.",
|
||||
@@ -432,9 +432,9 @@
|
||||
"UnsavedChanges": "Nicht gespeicherte Änderungen",
|
||||
"UnselectAll": "Alle abwählen",
|
||||
"UpdateAutomaticallyHelpText": "Updates automatisch herunterladen und installieren. Sie können weiterhin über System: Updates installieren",
|
||||
"UpdateCheckStartupNotWritableMessage": "Update kann nicht installiert werden, da der Startordner '{0}' vom Benutzer '{1}' nicht beschreibbar ist.",
|
||||
"UpdateCheckStartupTranslocationMessage": "Update kann nicht installiert werden, da sich der Startordner '{0}' in einem App Translocation-Ordner befindet.",
|
||||
"UpdateCheckUINotWritableMessage": "Update kann nicht installiert werden, da der Benutzeroberflächenordner '{0}' vom Benutzer '{1}' nicht beschreibbar ist.",
|
||||
"UpdateStartupNotWritableHealthCheckMessage": "Update kann nicht installiert werden, da der Startordner '{startupFolder}' vom Benutzer '{userName}' nicht beschreibbar ist.",
|
||||
"UpdateStartupTranslocationHealthCheckMessage": "Update kann nicht installiert werden, da sich der Startordner '{startupFolder}' in einem App Translocation-Ordner befindet.",
|
||||
"UpdateUiNotWritableHealthCheckMessage": "Update kann nicht installiert werden, da der Benutzeroberflächenordner '{uiFolder}' vom Benutzer '{userName}' nicht beschreibbar ist.",
|
||||
"UpdateMechanismHelpText": "Verwenden Sie den integrierten Updater von {appName} oder ein Skript",
|
||||
"UpdateScriptPathHelpText": "Pfad zu einem benutzerdefinierten Skript, das ein extrahiertes Update-Paket übernimmt und den Rest des Update-Prozesses abwickelt",
|
||||
"Updates": "Aktualisierung",
|
||||
@@ -485,7 +485,7 @@
|
||||
"More": "Mehr",
|
||||
"Publisher": "Herausgeber",
|
||||
"Track": "Trace",
|
||||
"UpdateAvailable": "Neue Version verfügbar",
|
||||
"UpdateAvailableHealthCheckMessage": "Neue Version verfügbar",
|
||||
"Year": "Jahr",
|
||||
"Album": "Album",
|
||||
"Artist": "Künstler",
|
||||
@@ -499,7 +499,7 @@
|
||||
"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: {0}",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Applikationen wegen folgender Fehler nicht verfügbar: {notificationNames}",
|
||||
"AuthBasic": "Basis (Browser-Popup)",
|
||||
"AuthForm": "Formulare (Anmeldeseite)",
|
||||
"DisabledForLocalAddresses": "Für lokale Adressen deaktiviert",
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
"EventType": "Είδος Γεγονότος",
|
||||
"Events": "Γεγονότα",
|
||||
"Edit": "Επεξεργασία",
|
||||
"DownloadClientStatusCheckSingleClientMessage": "Προγράμματα λήψης που είναι μη διαθέσιμα λόγων αποτυχιών: {0}",
|
||||
"DownloadClientStatusCheckAllClientMessage": "Όλα τα προγράμματα λήψης είναι μη διαθέσιμα λόγων αποτυχιών",
|
||||
"DownloadClientStatusSingleClientHealthCheckMessage": "Προγράμματα λήψης που είναι μη διαθέσιμα λόγων αποτυχιών: {downloadClientNames}",
|
||||
"DownloadClientStatusAllClientHealthCheckMessage": "Όλα τα προγράμματα λήψης είναι μη διαθέσιμα λόγων αποτυχιών",
|
||||
"DownloadClientsSettingsSummary": "Κάντε λήψη της διαμόρφωσης πελατών για ενσωμάτωση στην αναζήτηση διεπαφής χρήστη {appName}",
|
||||
"CustomFilters": "Custom Φιλτρα",
|
||||
"ConnectSettingsSummary": "Ειδοποιήσεις και προσαρμοσμένα σενάρια",
|
||||
@@ -45,7 +45,7 @@
|
||||
"Indexer": "Ευρετήριο",
|
||||
"PendingChangesDiscardChanges": "Απορρίψτε τις αλλαγές και φύγετε",
|
||||
"ShowSearchHelpText": "Εμφάνιση κουμπιού αναζήτησης στο δείκτη",
|
||||
"UpdateCheckStartupNotWritableMessage": "Δεν είναι δυνατή η εγκατάσταση της ενημέρωσης επειδή ο φάκελος εκκίνησης \"{0}\" δεν είναι εγγράψιμος από τον χρήστη \"{1}\".",
|
||||
"UpdateStartupNotWritableHealthCheckMessage": "Δεν είναι δυνατή η εγκατάσταση της ενημέρωσης επειδή ο φάκελος εκκίνησης \"{startupFolder}\" δεν είναι εγγράψιμος από τον χρήστη \"{userName}\".",
|
||||
"BranchUpdateMechanism": "Υποκατάστημα που χρησιμοποιείται από εξωτερικό μηχανισμό ενημέρωσης",
|
||||
"Mode": "Τρόπος",
|
||||
"SettingsEnableColorImpairedMode": "Ενεργοποίηση λειτουργίας με προβλήματα χρώματος",
|
||||
@@ -73,8 +73,8 @@
|
||||
"NoChange": "Καμία αλλαγή",
|
||||
"Port": "Λιμάνι",
|
||||
"PortNumber": "Αριθμός θύρας",
|
||||
"IndexerStatusCheckAllClientMessage": "Όλοι οι δείκτες δεν είναι διαθέσιμοι λόγω αστοχιών",
|
||||
"IndexerStatusCheckSingleClientMessage": "Τα ευρετήρια δεν είναι διαθέσιμα λόγω αστοχιών: {0}",
|
||||
"IndexerStatusAllUnavailableHealthCheckMessage": "Όλοι οι δείκτες δεν είναι διαθέσιμοι λόγω αστοχιών",
|
||||
"IndexerStatusUnavailableHealthCheckMessage": "Τα ευρετήρια δεν είναι διαθέσιμα λόγω αστοχιών: {indexerNames}",
|
||||
"KeyboardShortcuts": "Συντομεύσεις πληκτρολογίου",
|
||||
"Language": "Γλώσσα",
|
||||
"Reset": "Επαναφορά",
|
||||
@@ -113,7 +113,7 @@
|
||||
"ProxyBypassFilterHelpText": "Χρησιμοποιήστε το \",\" ως διαχωριστικό και \"*.\" ως μπαλαντέρ για υποτομείς",
|
||||
"UnableToAddANewAppProfilePleaseTryAgain": "Δεν είναι δυνατή η προσθήκη ενός νέου προφίλ ποιότητας. Δοκιμάστε ξανά.",
|
||||
"UnableToLoadHistory": "Δεν είναι δυνατή η φόρτωση του ιστορικού",
|
||||
"UpdateCheckUINotWritableMessage": "Δεν είναι δυνατή η εγκατάσταση της ενημέρωσης επειδή ο φάκελος διεπαφής χρήστη \"{0}\" δεν είναι εγγράψιμος από τον χρήστη \"{1}\".",
|
||||
"UpdateUiNotWritableHealthCheckMessage": "Δεν είναι δυνατή η εγκατάσταση της ενημέρωσης επειδή ο φάκελος διεπαφής χρήστη \"{uiFolder}\" δεν είναι εγγράψιμος από τον χρήστη \"{userName}\".",
|
||||
"AuthenticationMethodHelpText": "Απαιτήστε όνομα χρήστη και κωδικό πρόσβασης για πρόσβαση στο {appName}",
|
||||
"Automatic": "Αυτόματο",
|
||||
"BeforeUpdate": "Πριν από την ενημέρωση",
|
||||
@@ -139,10 +139,10 @@
|
||||
"HomePage": "Αρχική σελίδα",
|
||||
"Host": "Πλήθος",
|
||||
"Hostname": "Όνομα κεντρικού υπολογιστή",
|
||||
"IndexerLongTermStatusCheckSingleClientMessage": "Τα ευρετήρια δεν είναι διαθέσιμα λόγω αστοχιών για περισσότερο από 6 ώρες: {0}",
|
||||
"IndexerLongTermStatusUnavailableHealthCheckMessage": "Τα ευρετήρια δεν είναι διαθέσιμα λόγω αστοχιών για περισσότερο από 6 ώρες: {indexerNames}",
|
||||
"IndexerPriorityHelpText": "Προτεραιότητα ευρετηρίου από 1 (Υψηλότερη) έως 50 (Χαμηλότερη). Προεπιλογή: 25.",
|
||||
"IndexerProxyStatusCheckAllClientMessage": "Όλες οι λίστες δεν είναι διαθέσιμες λόγω αστοχιών",
|
||||
"IndexerProxyStatusCheckSingleClientMessage": "Τα ευρετήρια δεν είναι διαθέσιμα λόγω αστοχιών: {0}",
|
||||
"IndexerProxyStatusAllUnavailableHealthCheckMessage": "Όλες οι λίστες δεν είναι διαθέσιμες λόγω αστοχιών",
|
||||
"IndexerProxyStatusUnavailableHealthCheckMessage": "Τα ευρετήρια δεν είναι διαθέσιμα λόγω αστοχιών: {indexerProxyNames}",
|
||||
"LaunchBrowserHelpText": " Ανοίξτε ένα πρόγραμμα περιήγησης ιστού και μεταβείτε στην αρχική σελίδα του {appName} κατά την έναρξη της εφαρμογής.",
|
||||
"LogFiles": "Αρχεία καταγραφής",
|
||||
"Logging": "Ξύλευση",
|
||||
@@ -166,8 +166,8 @@
|
||||
"PendingChangesStayReview": "Παραμείνετε και ελέγξτε τις αλλαγές",
|
||||
"Presets": "Προεπιλογές",
|
||||
"Priority": "Προτεραιότητα",
|
||||
"ProxyCheckFailedToTestMessage": "Αποτυχία δοκιμής διακομιστή μεσολάβησης: {0}",
|
||||
"ProxyCheckResolveIpMessage": "Αποτυχία επίλυσης της διεύθυνσης IP για τον Διαμορφωμένο διακομιστή μεσολάβησης {0}",
|
||||
"ProxyFailedToTestHealthCheckMessage": "Αποτυχία δοκιμής διακομιστή μεσολάβησης: {url}",
|
||||
"ProxyResolveIpHealthCheckMessage": "Αποτυχία επίλυσης της διεύθυνσης IP για τον Διαμορφωμένο διακομιστή μεσολάβησης {proxyHostName}",
|
||||
"Queue": "Ουρά",
|
||||
"ReadTheWikiForMoreInformation": "Διαβάστε το Wiki για περισσότερες πληροφορίες",
|
||||
"Refresh": "Φρεσκάρω",
|
||||
@@ -219,7 +219,7 @@
|
||||
"UnableToLoadNotifications": "Δεν είναι δυνατή η φόρτωση ειδοποιήσεων",
|
||||
"UnableToLoadUISettings": "Δεν είναι δυνατή η φόρτωση των ρυθμίσεων διεπαφής χρήστη",
|
||||
"UnsavedChanges": "Μη αποθηκευμένες αλλαγές",
|
||||
"UpdateCheckStartupTranslocationMessage": "Δεν είναι δυνατή η εγκατάσταση της ενημέρωσης επειδή ο φάκελος εκκίνησης \"{0}\" βρίσκεται σε ένα φάκελο \"Μετατόπιση εφαρμογών\".",
|
||||
"UpdateStartupTranslocationHealthCheckMessage": "Δεν είναι δυνατή η εγκατάσταση της ενημέρωσης επειδή ο φάκελος εκκίνησης \"{startupFolder}\" βρίσκεται σε ένα φάκελο \"Μετατόπιση εφαρμογών\".",
|
||||
"UpdateScriptPathHelpText": "Διαδρομή σε ένα προσαρμοσμένο σενάριο που λαμβάνει ένα εξαγόμενο πακέτο ενημέρωσης και χειρίζεται το υπόλοιπο της διαδικασίας ενημέρωσης",
|
||||
"URLBase": "Βάση διεύθυνσης URL",
|
||||
"UrlBaseHelpText": "Για αντίστροφη υποστήριξη διακομιστή μεσολάβησης, η προεπιλογή είναι άδεια",
|
||||
@@ -242,10 +242,10 @@
|
||||
"BindAddress": "Δεσμευμένη διεύθυνση",
|
||||
"EnableRss": "Ενεργοποίηση RSS",
|
||||
"IndexerFlags": "Σημαίες ευρετηρίου",
|
||||
"IndexerLongTermStatusCheckAllClientMessage": "Όλοι οι δείκτες δεν είναι διαθέσιμοι λόγω αστοχιών για περισσότερο από 6 ώρες",
|
||||
"IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Όλοι οι δείκτες δεν είναι διαθέσιμοι λόγω αστοχιών για περισσότερο από 6 ώρες",
|
||||
"InteractiveSearch": "Διαδραστική αναζήτηση",
|
||||
"Interval": "Διάστημα",
|
||||
"ProxyCheckBadRequestMessage": "Αποτυχία δοκιμής διακομιστή μεσολάβησης. StatusCode: {0}",
|
||||
"ProxyBadRequestHealthCheckMessage": "Αποτυχία δοκιμής διακομιστή μεσολάβησης. StatusCode: {statusCode}",
|
||||
"ProxyPasswordHelpText": "Πρέπει να εισαγάγετε ένα όνομα χρήστη και έναν κωδικό πρόσβασης μόνο εάν απαιτείται. Αφήστε τα κενά διαφορετικά.",
|
||||
"ProxyType": "Τύπος διακομιστή μεσολάβησης",
|
||||
"ProxyUsernameHelpText": "Πρέπει να εισαγάγετε ένα όνομα χρήστη και έναν κωδικό πρόσβασης μόνο εάν απαιτείται. Αφήστε τα κενά διαφορετικά.",
|
||||
@@ -356,7 +356,7 @@
|
||||
"Auth": "Auth",
|
||||
"BookSearch": "Αναζήτηση βιβλίου",
|
||||
"FullSync": "Πλήρης συγχρονισμός",
|
||||
"IndexerVipCheckExpiringClientMessage": "Τα οφέλη VIP του ευρετηρίου λήγουν σύντομα: {0}",
|
||||
"IndexerVipExpiringHealthCheckMessage": "Τα οφέλη VIP του ευρετηρίου λήγουν σύντομα: {indexerNames}",
|
||||
"NotSupported": "Δεν υποστηρίζεται",
|
||||
"Parameters": "Παράμετροι",
|
||||
"Public": "Δημόσιο",
|
||||
@@ -394,7 +394,7 @@
|
||||
"IndexerQuery": "Ερώτημα ευρετηρίου",
|
||||
"IndexerSettingsSummary": "Διαμορφώστε διάφορες καθολικές ρυθμίσεις ευρετηρίου, συμπεριλαμβανομένων των διακομιστών μεσολάβησης.",
|
||||
"IndexerSite": "Ιστότοπος ευρετηρίου",
|
||||
"IndexerVipCheckExpiredClientMessage": "Τα προνόμια VIP του ευρετηρίου έχουν λήξει: {0}",
|
||||
"IndexerVipExpiredHealthCheckMessage": "Τα προνόμια VIP του ευρετηρίου έχουν λήξει: {indexerNames}",
|
||||
"MappedCategories": "Χαρτογραφημένες κατηγορίες",
|
||||
"MovieSearch": "Αναζήτηση ταινίας",
|
||||
"MovieSearchTypes": "Τύποι αναζήτησης ταινιών",
|
||||
@@ -459,7 +459,7 @@
|
||||
"Remove": "Αφαιρώ",
|
||||
"Replace": "Αντικαθιστώ",
|
||||
"TheLatestVersionIsAlreadyInstalled": "Η τελευταία έκδοση του {appName} είναι ήδη εγκατεστημένη",
|
||||
"ApiKeyValidationHealthCheckMessage": "Παρακαλούμε ενημερώστε το κλείδι API ώστε να έχει τουλάχιστον {0} χαρακτήρες. Μπορείτε να το κάνετε αυτό μέσα από τις ρυθμίσεις ή το αρχείο ρυθμίσεων",
|
||||
"ApiKeyValidationHealthCheckMessage": "Παρακαλούμε ενημερώστε το κλείδι API ώστε να έχει τουλάχιστον {length} χαρακτήρες. Μπορείτε να το κάνετε αυτό μέσα από τις ρυθμίσεις ή το αρχείο ρυθμίσεων",
|
||||
"StopSelecting": "Διακοπή Επιλογής",
|
||||
"OnHealthRestored": "Στην Αποκατάσταση Υγείας",
|
||||
"ApplicationURL": "Διεύθυνση URL εφαρμογής",
|
||||
@@ -485,7 +485,7 @@
|
||||
"Theme": "Θέμα",
|
||||
"Track": "Ιχνος",
|
||||
"Year": "Ετος",
|
||||
"UpdateAvailable": "Νέα ενημέρωση είναι διαθέσιμη",
|
||||
"UpdateAvailableHealthCheckMessage": "Νέα ενημέρωση είναι διαθέσιμη",
|
||||
"Artist": "Καλλιτέχνης",
|
||||
"Author": "Συγγραφέας",
|
||||
"Book": "Βιβλίο",
|
||||
@@ -501,7 +501,7 @@
|
||||
"DeleteAppProfileMessageText": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το προφίλ ποιότητας '{0}'?",
|
||||
"AddConnection": "Προσθήκη Σύνδεσης",
|
||||
"NotificationStatusAllClientHealthCheckMessage": "Όλες οι λίστες δεν είναι διαθέσιμες λόγω αστοχιών",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Μη διαθέσιμες λίστες λόγω αποτυχιών: {0}",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Μη διαθέσιμες λίστες λόγω αποτυχιών: {notificationNames}",
|
||||
"AuthBasic": "Βασικό (Αναδυόμενο παράθυρο προγράμματος περιήγησης)",
|
||||
"AuthForm": "Φόρμες (σελίδα σύνδεσης)",
|
||||
"Clone": "Κλωνοποίηση",
|
||||
@@ -518,5 +518,9 @@
|
||||
"AddConnectionImplementation": "Προσθήκη - {implementationName}",
|
||||
"AddIndexerImplementation": "Προσθήκη",
|
||||
"EditIndexerProxyImplementation": "Προσθήκη",
|
||||
"CountApplicationsSelected": "Επιλέχθηκαν {0} συλλογές"
|
||||
"CountApplicationsSelected": "Επιλέχθηκαν {0} συλλογές",
|
||||
"IndexerBeyondHDSettingsSearchTypes": "Τύποι αναζήτησης",
|
||||
"IndexerHDBitsSettingsMediums": "Μεσαίο",
|
||||
"UseSsl": "Χρησιμοποιήστε SSL",
|
||||
"CustomFilter": "Custom Φιλτρα"
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"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.",
|
||||
"ApiKey": "API Key",
|
||||
"ApiKeyValidationHealthCheckMessage": "Please update your API key to be at least {0} characters long. You can do this via settings or the config file",
|
||||
"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",
|
||||
"AppDataLocationHealthCheckMessage": "Updating will not be possible to prevent deleting AppData on Update",
|
||||
"AppProfileInUse": "App Profile in Use",
|
||||
@@ -45,6 +45,8 @@
|
||||
"Application": "Application",
|
||||
"ApplicationLongTermStatusCheckAllClientMessage": "All applications are unavailable due to failures for more than 6 hours",
|
||||
"ApplicationLongTermStatusCheckSingleClientMessage": "Applications unavailable due to failures for more than 6 hours: {0}",
|
||||
"ApplicationSettingsSyncRejectBlocklistedTorrentHashes": "Sync Reject Blocklisted Torrent Hashes While Grabbing",
|
||||
"ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "If a torrent is blocked by hash it may not properly be rejected during RSS/Search for some indexers, enabling this will allow it to be rejected after the torrent is grabbed, but before it is sent to the client.",
|
||||
"ApplicationStatusCheckAllClientMessage": "All applications are unavailable due to failures",
|
||||
"ApplicationStatusCheckSingleClientMessage": "Applications unavailable due to failures: {0}",
|
||||
"ApplicationTagsHelpText": "Sync Indexers to this application that have one or more matching tags. If no tags are listed here, then no indexers will be prevented from syncing due to their tags.",
|
||||
@@ -114,6 +116,7 @@
|
||||
"Clear": "Clear",
|
||||
"ClearHistory": "Clear History",
|
||||
"ClearHistoryMessageText": "Are you sure you want to clear all {appName} history?",
|
||||
"ClickToChangeQueryOptions": "Click to change query options",
|
||||
"ClientPriority": "Client Priority",
|
||||
"Clone": "Clone",
|
||||
"CloneProfile": "Clone Profile",
|
||||
@@ -223,8 +226,8 @@
|
||||
"DownloadClientSettingsPriorityItemHelpText": "Priority to use when grabbing items",
|
||||
"DownloadClientSettingsUrlBaseHelpText": "Adds a prefix to the {clientName} url, such as {url}",
|
||||
"DownloadClientSettingsUseSslHelpText": "Use secure connection when connection to {clientName}",
|
||||
"DownloadClientStatusCheckAllClientMessage": "All download clients are unavailable due to failures",
|
||||
"DownloadClientStatusCheckSingleClientMessage": "Download clients unavailable due to failures: {0}",
|
||||
"DownloadClientStatusAllClientHealthCheckMessage": "All download clients are unavailable due to failures",
|
||||
"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}'",
|
||||
"DownloadClients": "Download Clients",
|
||||
@@ -324,7 +327,7 @@
|
||||
"IndexerCategories": "Indexer Categories",
|
||||
"IndexerDetails": "Indexer Details",
|
||||
"IndexerDisabled": "Indexer Disabled",
|
||||
"IndexerDownloadClientHealthCheckMessage": "Indexers with invalid download clients: {0}.",
|
||||
"IndexerDownloadClientHealthCheckMessage": "Indexers with invalid download clients: {indexerNames}.",
|
||||
"IndexerDownloadClientHelpText": "Specify which download client is used for grabs made within {appName} from this indexer",
|
||||
"IndexerFailureRate": "Indexer Failure Rate",
|
||||
"IndexerFileListSettingsFreeleechOnlyHelpText": "Search freeleech releases only",
|
||||
@@ -353,8 +356,10 @@
|
||||
"IndexerIPTorrentsSettingsFreeleechOnlyHelpText": "Search freeleech releases only",
|
||||
"IndexerId": "Indexer ID",
|
||||
"IndexerInfo": "Indexer Info",
|
||||
"IndexerLongTermStatusCheckAllClientMessage": "All indexers are unavailable due to failures for more than 6 hours",
|
||||
"IndexerLongTermStatusCheckSingleClientMessage": "Indexers unavailable due to failures for more than 6 hours: {0}",
|
||||
"IndexerLongTermStatusAllUnavailableHealthCheckMessage": "All indexers are unavailable due to failures for more than 6 hours",
|
||||
"IndexerLongTermStatusUnavailableHealthCheckMessage": "Indexers unavailable due to failures for more than 6 hours: {indexerNames}",
|
||||
"IndexerMTeamTpSettingsApiKeyHelpText": "API Key from the Site (Found in User Control Panel => Security => Laboratory)",
|
||||
"IndexerMTeamTpSettingsFreeleechOnlyHelpText": "Search freeleech releases only",
|
||||
"IndexerName": "Indexer Name",
|
||||
"IndexerNebulanceSettingsApiKeyHelpText": "API Key from User Settings > Api Keys. Key must have List and Download permissions",
|
||||
"IndexerNewznabSettingsAdditionalParametersHelpText": "Additional Newznab parameters",
|
||||
@@ -371,8 +376,8 @@
|
||||
"IndexerPriorityHelpText": "Indexer Priority from 1 (Highest) to 50 (Lowest). Default: 25.",
|
||||
"IndexerProxies": "Indexer Proxies",
|
||||
"IndexerProxy": "Indexer Proxy",
|
||||
"IndexerProxyStatusCheckAllClientMessage": "All proxies are unavailable due to failures",
|
||||
"IndexerProxyStatusCheckSingleClientMessage": "Proxies unavailable due to failures: {0}",
|
||||
"IndexerProxyStatusAllUnavailableHealthCheckMessage": "All indexer proxies are unavailable due to failures",
|
||||
"IndexerProxyStatusUnavailableHealthCheckMessage": "Indexer proxies unavailable due to failures: {indexerProxyNames}",
|
||||
"IndexerQuery": "Indexer Query",
|
||||
"IndexerRedactedSettingsApiKeyHelpText": "API Key from the Site (Found in Settings => Access Settings)",
|
||||
"IndexerRss": "Indexer RSS",
|
||||
@@ -396,8 +401,6 @@
|
||||
"IndexerSettingsPasskey": "Pass Key",
|
||||
"IndexerSettingsQueryLimit": "Query Limit",
|
||||
"IndexerSettingsQueryLimitHelpText": "The number of max queries as specified by the respective unit that {appName} will allow to the site",
|
||||
"IndexerSettingsRejectBlocklistedTorrentHashes": "Reject Blocklisted Torrent Hashes While Grabbing",
|
||||
"IndexerSettingsRejectBlocklistedTorrentHashesHelpText": "If a torrent is blocked by hash it may not properly be rejected during RSS/Search for some indexers, enabling this will allow it to be rejected after the torrent is grabbed, but before it is sent to the client.",
|
||||
"IndexerSettingsRssKey": "RSS Key",
|
||||
"IndexerSettingsSeedRatio": "Seed Ratio",
|
||||
"IndexerSettingsSeedRatioHelpText": "The ratio a torrent should reach before stopping, empty uses the download client's default. Ratio should be at least 1.0 and follow the indexers rules",
|
||||
@@ -407,13 +410,13 @@
|
||||
"IndexerSettingsVipExpiration": "VIP Expiration",
|
||||
"IndexerSite": "Indexer Site",
|
||||
"IndexerStatus": "Indexer Status",
|
||||
"IndexerStatusCheckAllClientMessage": "All indexers are unavailable due to failures",
|
||||
"IndexerStatusCheckSingleClientMessage": "Indexers unavailable due to failures: {0}",
|
||||
"IndexerStatusAllUnavailableHealthCheckMessage": "All indexers are unavailable due to failures",
|
||||
"IndexerStatusUnavailableHealthCheckMessage": "Indexers unavailable due to failures: {indexerNames}",
|
||||
"IndexerTagsHelpText": "Use tags to specify Indexer Proxies or which apps the indexer is synced to.",
|
||||
"IndexerTagsHelpTextWarning": "Tags should be used with caution, they can have unintended effects. An indexer with a tag will only sync to apps with the same tag.",
|
||||
"IndexerTorrentSyndikatSettingsApiKeyHelpText": "Site API Key",
|
||||
"IndexerVipCheckExpiredClientMessage": "Indexer VIP benefits have expired: {0}",
|
||||
"IndexerVipCheckExpiringClientMessage": "Indexer VIP benefits expiring soon: {0}",
|
||||
"IndexerVipExpiredHealthCheckMessage": "Indexer VIP benefits have expired: {indexerNames}",
|
||||
"IndexerVipExpiringHealthCheckMessage": "Indexer VIP benefits expiring soon: {indexerNames}",
|
||||
"Indexers": "Indexers",
|
||||
"Info": "Info",
|
||||
"InitialFailure": "Initial Failure",
|
||||
@@ -486,12 +489,14 @@
|
||||
"NotSupported": "Not Supported",
|
||||
"Notification": "Notification",
|
||||
"NotificationStatusAllClientHealthCheckMessage": "All notifications are unavailable due to failures",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Notifications unavailable due to failures: {0}",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Notifications unavailable due to failures: {notificationNames}",
|
||||
"NotificationTriggers": "Notification Triggers",
|
||||
"NotificationTriggersHelpText": "Select which events should trigger this notification",
|
||||
"Notifications": "Notifications",
|
||||
"NotificationsEmailSettingsUseEncryption": "Use Encryption",
|
||||
"NotificationsEmailSettingsUseEncryptionHelpText": "Whether to prefer using encryption if configured on the server, to always use encryption via SSL (Port 465 only) or StartTLS (any other port) or to never use encryption",
|
||||
"NotificationsTelegramSettingsIncludeAppName": "Include {appName} in Title",
|
||||
"NotificationsTelegramSettingsIncludeAppNameHelpText": "Optionally prefix message title with {appName} to differentiate notifications from different applications",
|
||||
"OAuthPopupMessage": "Pop-ups are being blocked by your browser",
|
||||
"Ok": "Ok",
|
||||
"OnApplicationUpdate": "On Application Update",
|
||||
@@ -530,11 +535,11 @@
|
||||
"ProwlarrSupportsAnyIndexer": "{appName} supports many indexers in addition to any indexer that uses the Newznab/Torznab standard using 'Generic Newznab' (for usenet) or 'Generic Torznab' (for torrents). Search & Select your indexer from below.",
|
||||
"Proxies": "Proxies",
|
||||
"Proxy": "Proxy",
|
||||
"ProxyBadRequestHealthCheckMessage": "Failed to test proxy. Status code: {statusCode}",
|
||||
"ProxyBypassFilterHelpText": "Use ',' as a separator, and '*.' as a wildcard for subdomains",
|
||||
"ProxyCheckBadRequestMessage": "Failed to test proxy. Status code: {0}",
|
||||
"ProxyCheckFailedToTestMessage": "Failed to test proxy: {0}",
|
||||
"ProxyCheckResolveIpMessage": "Failed to resolve the IP Address for the Configured Proxy Host {0}",
|
||||
"ProxyFailedToTestHealthCheckMessage": "Failed to test proxy: {url}",
|
||||
"ProxyPasswordHelpText": "You only need to enter a username and password if one is required. Leave them blank otherwise.",
|
||||
"ProxyResolveIpHealthCheckMessage": "Failed to resolve the IP Address for the Configured Proxy Host {proxyHostName}",
|
||||
"ProxyType": "Proxy Type",
|
||||
"ProxyUsernameHelpText": "You only need to enter a username and password if one is required. Leave them blank otherwise.",
|
||||
"Public": "Public",
|
||||
@@ -715,12 +720,12 @@
|
||||
"UnsavedChanges": "Unsaved Changes",
|
||||
"UnselectAll": "Unselect All",
|
||||
"UpdateAutomaticallyHelpText": "Automatically download and install updates. You will still be able to install from System: Updates",
|
||||
"UpdateAvailable": "New update is available",
|
||||
"UpdateCheckStartupNotWritableMessage": "Cannot install update because startup folder '{0}' is not writable by the user '{1}'.",
|
||||
"UpdateCheckStartupTranslocationMessage": "Cannot install update because startup folder '{0}' is in an App Translocation folder.",
|
||||
"UpdateCheckUINotWritableMessage": "Cannot install update because UI folder '{0}' is not writable by the user '{1}'.",
|
||||
"UpdateAvailableHealthCheckMessage": "New update is available",
|
||||
"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}'.",
|
||||
"Updates": "Updates",
|
||||
"Uptime": "Uptime",
|
||||
"Url": "Url",
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
"Files": "Archivos",
|
||||
"Events": "Eventos",
|
||||
"Edit": "Editar",
|
||||
"DownloadClientStatusCheckSingleClientMessage": "Gestores de descargas no disponibles debido a errores: {0}",
|
||||
"DownloadClientStatusCheckAllClientMessage": "Los gestores de descargas no están disponibles debido a errores",
|
||||
"DownloadClientStatusSingleClientHealthCheckMessage": "Gestores de descargas no disponibles debido a errores: {downloadClientNames}",
|
||||
"DownloadClientStatusAllClientHealthCheckMessage": "Los gestores de descargas no están disponibles debido a errores",
|
||||
"DownloadClients": "Clientes de descarga",
|
||||
"Delete": "Eliminar",
|
||||
"Dates": "Fechas",
|
||||
@@ -28,9 +28,9 @@
|
||||
"About": "Acerca de",
|
||||
"View": "Vista",
|
||||
"Updates": "Actualizaciones",
|
||||
"UpdateCheckUINotWritableMessage": "No se puede instalar la actualización porque la carpeta UI '{0}' no tiene permisos de escritura para el usuario '{1}'.",
|
||||
"UpdateCheckStartupTranslocationMessage": "No se puede instalar la actualización porque la carpeta de arranque '{0}' está en una carpeta de \"App Translocation\".",
|
||||
"UpdateCheckStartupNotWritableMessage": "No se puede instalar la actualización porque la carpeta de arranque '{0}' no tiene permisos de escritura para el usuario '{1}'.",
|
||||
"UpdateUiNotWritableHealthCheckMessage": "No se puede instalar la actualización porque la carpeta UI '{uiFolder}' no tiene permisos de escritura para el usuario '{userName}'.",
|
||||
"UpdateStartupTranslocationHealthCheckMessage": "No se puede instalar la actualización porque la carpeta de arranque '{startupFolder}' está en una carpeta de \"App Translocation\".",
|
||||
"UpdateStartupNotWritableHealthCheckMessage": "No se puede instalar la actualización porque la carpeta de arranque '{startupFolder}' no tiene permisos de escritura para el usuario '{userName}'.",
|
||||
"UnselectAll": "Desmarcar todo",
|
||||
"UI": "UI",
|
||||
"Tasks": "Tareas",
|
||||
@@ -51,9 +51,9 @@
|
||||
"ReleaseBranchCheckOfficialBranchMessage": "Las versión {0} no es una versión válida de {appName}, no recibirás actualizaciones",
|
||||
"Refresh": "Actualizar",
|
||||
"Queue": "Cola",
|
||||
"ProxyCheckResolveIpMessage": "No se pudo resolver la dirección IP del Host Proxy configurado {0}",
|
||||
"ProxyCheckFailedToTestMessage": "Fallo al comprobar el proxy: {0}",
|
||||
"ProxyCheckBadRequestMessage": "Fallo al comprobar el proxy. Status code: {0}",
|
||||
"ProxyResolveIpHealthCheckMessage": "No se pudo resolver la dirección IP del Host Proxy configurado {proxyHostName}",
|
||||
"ProxyFailedToTestHealthCheckMessage": "Fallo al comprobar el proxy: {url}",
|
||||
"ProxyBadRequestHealthCheckMessage": "Fallo al comprobar el proxy. Status code: {statusCode}",
|
||||
"Proxy": "Proxy",
|
||||
"Options": "Opciones",
|
||||
"NoChange": "Sin cambio",
|
||||
@@ -62,7 +62,7 @@
|
||||
"Logging": "Registro de eventos",
|
||||
"LogFiles": "Archivos de Registro",
|
||||
"Language": "Idioma",
|
||||
"IndexerStatusCheckAllClientMessage": "Todos los indexadores no están disponibles debido a errores",
|
||||
"IndexerStatusAllUnavailableHealthCheckMessage": "Todos los indexadores no están disponibles debido a errores",
|
||||
"Added": "Añadido",
|
||||
"Actions": "Acciones",
|
||||
"UISettingsSummary": "Fecha, idioma, y opciones de color deteriorado",
|
||||
@@ -71,7 +71,7 @@
|
||||
"ReleaseStatus": "Estado del Estreno",
|
||||
"Protocol": "Protocolo",
|
||||
"LastWriteTime": "Última Fecha de Escritura",
|
||||
"IndexerStatusCheckSingleClientMessage": "Indexadores no disponibles debido a errores: {indexerNames}",
|
||||
"IndexerStatusUnavailableHealthCheckMessage": "Indexadores no disponibles debido a errores: {indexerNames}",
|
||||
"Indexer": "Indexador",
|
||||
"Grabbed": "Añadido",
|
||||
"GeneralSettingsSummary": "Puerto, SSL, nombre de usuario/contraseña , proxy, analíticas, y actualizaciones",
|
||||
@@ -284,8 +284,8 @@
|
||||
"MovieIndexScrollBottom": "Indice de Películas: Desplazar hacia abajo",
|
||||
"CloseCurrentModal": "Cerrar esta Ventana Modal",
|
||||
"AcceptConfirmationModal": "Aceptar Confirmación de esta Ventana Modal",
|
||||
"IndexerLongTermStatusCheckSingleClientMessage": "Indexers no disponible por errores durando más de 6 horas: {0}",
|
||||
"IndexerLongTermStatusCheckAllClientMessage": "Ningún indexer está disponible por errores durando más de 6 horas",
|
||||
"IndexerLongTermStatusUnavailableHealthCheckMessage": "Indexers no disponible por errores durando más de 6 horas: {indexerNames}",
|
||||
"IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Ningún indexer está disponible por errores durando más de 6 horas",
|
||||
"Reddit": "Reddit",
|
||||
"UnableToAddANewAppProfilePleaseTryAgain": "No se ha podido añadir un nuevo perfil de calidad, prueba otra vez.",
|
||||
"DeleteIndexerProxyMessageText": "¿Seguro que quieres eliminar el proxy indexador '{name}'?",
|
||||
@@ -308,8 +308,8 @@
|
||||
"ApplicationStatusCheckSingleClientMessage": "Listas no disponibles debido a errores: {0}",
|
||||
"AllIndexersHiddenDueToFilter": "Todos los indexadores están ocultas debido al filtro aplicado.",
|
||||
"DeleteApplicationMessageText": "Seguro que quieres eliminar la notificación '{name}'?",
|
||||
"IndexerProxyStatusCheckAllClientMessage": "Los indexers no están disponibles debido a errores",
|
||||
"IndexerProxyStatusCheckSingleClientMessage": "Indexers no disponibles debido a errores: {0}",
|
||||
"IndexerProxyStatusAllUnavailableHealthCheckMessage": "Los indexers no están disponibles debido a errores",
|
||||
"IndexerProxyStatusUnavailableHealthCheckMessage": "Indexers no disponibles debido a errores: {indexerProxyNames}",
|
||||
"NoLinks": "Sin enlaces",
|
||||
"AddDownloadClient": "Añadir Cliente de Descarga",
|
||||
"CouldNotConnectSignalR": "No se pudo conectar a SignalR, la interfaz de usuario no se actualiza",
|
||||
@@ -388,7 +388,7 @@
|
||||
"More": "Más",
|
||||
"Track": "Rastro",
|
||||
"Year": "Año",
|
||||
"UpdateAvailable": "La nueva actualización está disponible",
|
||||
"UpdateAvailableHealthCheckMessage": "La nueva actualización está disponible",
|
||||
"Genre": "Género",
|
||||
"Publisher": "Editor",
|
||||
"AuthenticationRequired": "Autenticación requerida",
|
||||
@@ -399,8 +399,8 @@
|
||||
"EditSelectedIndexers": "Editar Indexadores Seleccionados",
|
||||
"Implementation": "Implementación",
|
||||
"ManageDownloadClients": "Gestionar Clientes de Descarga",
|
||||
"ApiKeyValidationHealthCheckMessage": "Actualice su clave de API para que tenga al menos {0} carácteres. Puede hacerlo en los ajustes o en el archivo de configuración",
|
||||
"IndexerDownloadClientHealthCheckMessage": "Indexadores con clientes de descarga inválidos: {0}.",
|
||||
"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",
|
||||
"ConnectionLostReconnect": "{appName} intentará conectarse automáticamente, o puede hacer clic en recargar abajo.",
|
||||
"ConnectionLostToBackend": "{appName} ha perdido su conexión con el backend y tendrá que ser recargado para recuperar su funcionalidad.",
|
||||
@@ -412,7 +412,7 @@
|
||||
"DeleteAppProfileMessageText": "¿Estás seguro de que quieres eliminar el perfil de la aplicación '{name}'?",
|
||||
"AddConnection": "Añadir Conexión",
|
||||
"NotificationStatusAllClientHealthCheckMessage": "Las notificaciones no están disponibles debido a fallos",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Listas no disponibles debido a errores: {0}",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Listas no disponibles debido a errores: {notificationNames}",
|
||||
"EditIndexerImplementation": "Editar Indexador - {implementationName}",
|
||||
"AuthBasic": "Básico (ventana emergente del navegador)",
|
||||
"AuthForm": "Formularios (Página de inicio de sesión)",
|
||||
@@ -423,14 +423,14 @@
|
||||
"External": "Externo",
|
||||
"None": "Ninguno",
|
||||
"ResetAPIKeyMessageText": "¿Estás seguro que quieres restablecer tu clave API?",
|
||||
"EditIndexerProxyImplementation": "Editar Proxy de Indexador - { implementationName}",
|
||||
"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}",
|
||||
"AddApplicationImplementation": "Agregar aplicación - { implementationName}",
|
||||
"AddApplicationImplementation": "Agregar aplicación - {implementationName}",
|
||||
"AddConnectionImplementation": "Añadir Conexión - {implementationName}",
|
||||
"AddIndexerImplementation": "Agregar Indexador - {implementationName}",
|
||||
"AddIndexerProxyImplementation": "Agregar Proxy de Indexador - { implementationName}",
|
||||
"EditApplicationImplementation": "Editar Aplicación - { implementationName}",
|
||||
"EditApplicationImplementation": "Editar aplicación - {implementationName}",
|
||||
"EditConnectionImplementation": "Editar Conexión - {implementationName}",
|
||||
"AddDownloadClientImplementation": "Añadir Cliente de Descarga - {implementationName}",
|
||||
"AuthenticationMethod": "Método de autenticación",
|
||||
@@ -524,10 +524,10 @@
|
||||
"IndexerRss": "RSS del Indexador",
|
||||
"IndexerSettingsSummary": "Configurar varios ajustes globales del Indexador, incluyendo Proxies.",
|
||||
"IndexerName": "Nombre del Indexador",
|
||||
"IndexerVipCheckExpiringClientMessage": "Las ventajas VIP de los indexadores expiran pronto: {0}",
|
||||
"IndexerVipExpiringHealthCheckMessage": "Las ventajas VIP de los indexadores expiran pronto: {indexerNames}",
|
||||
"IndexerProxies": "Proxies del Indexador",
|
||||
"IndexerHistoryLoadError": "Error al cargar el historial del Indexador",
|
||||
"IndexerVipCheckExpiredClientMessage": "Las ventajas VIP del Indexador han caducado: {0}",
|
||||
"IndexerVipExpiredHealthCheckMessage": "Las ventajas VIP del Indexador han caducado: {indexerNames}",
|
||||
"IndexerQuery": "Consulta dedl Indexador",
|
||||
"IndexerSite": "Sitio del Indexador",
|
||||
"IndexerStatus": "Estado del indexador",
|
||||
@@ -621,5 +621,131 @@
|
||||
"UsenetBlackholeNzbFolder": "Carpeta Nzb",
|
||||
"TorrentBlackholeSaveMagnetFilesHelpText": "Guarda el enlace magnet si no hay ningún archivo .torrent disponible (útil solo si el cliente de descarga soporta magnets guardados en un archivo)",
|
||||
"SecretToken": "Token secreto",
|
||||
"TorrentBlackholeSaveMagnetFilesExtension": "Guardar extensión de archivos magnet"
|
||||
"TorrentBlackholeSaveMagnetFilesExtension": "Guardar extensión de archivos magnet",
|
||||
"Donate": "Donar",
|
||||
"ClickToChangeQueryOptions": "Haz clic para cambiar las opciones de petición",
|
||||
"DefaultCategory": "Categoría predeterminada",
|
||||
"Destination": "Destino",
|
||||
"Directory": "Directorio",
|
||||
"DownloadClientDelugeSettingsUrlBaseHelpText": "Añade un prefijo al url del json de deluge, vea {url}",
|
||||
"DownloadClientDownloadStationSettingsDirectoryHelpText": "Carpeta compartida opcional en la que poner las descargas, dejar en blanco para usar la ubicación de la Estación de Descarga predeterminada",
|
||||
"DownloadClientFloodSettingsAdditionalTags": "Etiquetas adicionales",
|
||||
"DownloadClientFloodSettingsAdditionalTagsHelpText": "Añade propiedades de medios como etiquetas. Los consejos son ejemplos.",
|
||||
"DownloadClientFreeboxSettingsAppId": "ID de la app",
|
||||
"DownloadClientFreeboxSettingsAppToken": "Token de la app",
|
||||
"DownloadClientFreeboxSettingsApiUrl": "URL de API",
|
||||
"DownloadClientFreeboxSettingsApiUrlHelpText": "Define la URL base de la API Freebox con la versión de la API, p. ej. '{url}', por defecto a '{defaultApiUrl}'",
|
||||
"DownloadClientFreeboxSettingsAppIdHelpText": "ID de la app dada cuando se crea acceso a la API de Freebox (esto es 'app_id')",
|
||||
"DownloadClientFloodSettingsTagsHelpText": "Etiquetas iniciales de una descarga. Para ser reconocida, una descarga debe tener todas las etiquetas iniciales. Esto evita conflictos con descargas no relacionadas.",
|
||||
"DownloadClientFloodSettingsUrlBaseHelpText": "Añade un prefijo a la API de Flood, como {url}",
|
||||
"DownloadClientFreeboxSettingsAppTokenHelpText": "Token de la app recuperado cuando se crea acceso a la API de Freebox (esto es 'app_token')",
|
||||
"ProwlarrDownloadClientsAlert": "Si intentas hacer búsquedas directamente dentro de {appName}, necesitas añadir clientes de descarga. De otro modo, no necesitas añadirlos aquí. Para búsquedas desde tus aplicaciones, los clientes de descarga configurados serán usados en su lugar.",
|
||||
"ProwlarrDownloadClientsInAppOnlyAlert": "Los clientes de descarga son solo para búsquedas internas en {appName} y no sincronizan a las aplicaciones. No hay planes para añadir cualquier funcionalidad.",
|
||||
"TorrentBlackholeSaveMagnetFilesExtensionHelpText": "Extensión a usar para enlaces magnet, predeterminado a '.magnet'",
|
||||
"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",
|
||||
"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",
|
||||
"IndexerHDBitsSettingsOriginsHelpText": "Si no se especifica, se usarán todas las opciones.",
|
||||
"IndexerIPTorrentsSettingsCookieUserAgent": "Cookie de User-Agent",
|
||||
"IndexerAlphaRatioSettingsExcludeSceneHelpText": "Excluye lanzamientos de ESCENA de los resultados",
|
||||
"IndexerAlphaRatioSettingsExcludeScene": "Excluir ESCENA",
|
||||
"IndexerAlphaRatioSettingsFreeleechOnlyHelpText": "Buscar solo lanzamientos freeleech",
|
||||
"IndexerBeyondHDSettingsLimitedOnly": "Solo limitados",
|
||||
"IndexerBeyondHDSettingsRewindOnlyHelpText": "Buscar solo rebobinados",
|
||||
"IndexerBeyondHDSettingsRssKeyHelpText": "Clave RSS del sitio (encontrada en Mi seguridad => Clave RSS)",
|
||||
"IndexerBeyondHDSettingsSearchTypes": "Tipos de búsqueda",
|
||||
"IndexerGazelleGamesSettingsSearchGroupNames": "Buscar nombres de grupo",
|
||||
"IndexerHDBitsSettingsCodecsHelpText": "Si no se especifica, se usarán todas las opciones.",
|
||||
"IndexerHDBitsSettingsMediumsHelpText": "Si no se especifica, se usarán todas las opciones.",
|
||||
"IndexerHDBitsSettingsUseFilenames": "Usar nombres de archivo",
|
||||
"IndexerHDBitsSettingsUseFilenamesHelpText": "Señala esta opción si quieres usar nombres de archivo como títulos de lanzamiento",
|
||||
"IndexerHDBitsSettingsUsernameHelpText": "Usuario del sitio",
|
||||
"IndexerIPTorrentsSettingsFreeleechOnlyHelpText": "Buscar solo lanzamientos freeleech",
|
||||
"IndexerNewznabSettingsAdditionalParametersHelpText": "Parámetros adicionales de Newznab",
|
||||
"IndexerNewznabSettingsApiKeyHelpText": "Clave API del sitio",
|
||||
"IndexerNzbIndexSettingsApiKeyHelpText": "Clave API del sitio",
|
||||
"IndexerPassThePopcornSettingsApiUserHelpText": "Estas opciones se encuentran en tus opciones de seguridad de PassThePopcorn (Editar perfil > Seguridad).",
|
||||
"IndexerPassThePopcornSettingsApiKeyHelpText": "Clave API del sitio",
|
||||
"IndexerRedactedSettingsApiKeyHelpText": "Clave API del sitio (encontrada en Opciones => Opciones de acceso)",
|
||||
"IndexerSettingsAdditionalParameters": "Parámetros adicionales",
|
||||
"IndexerSettingsApiPathHelpText": "Ruta a la API, usualmente {url}",
|
||||
"IndexerSettingsApiUser": "Usuario de API",
|
||||
"IndexerSettingsAppsMinimumSeeders": "Semillas mínimas de las aplicaciones",
|
||||
"IndexerSettingsBaseUrlHelpText": "Selecciona qué url base usará {appName} para las peticiones al sitio",
|
||||
"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",
|
||||
"IndexerMTeamTpSettingsApiKeyHelpText": "Clave API del sitio (encontrada en Panel de control de usuario => Seguridad => Laboratorio)",
|
||||
"IndexerMTeamTpSettingsFreeleechOnlyHelpText": "Buscar solo lanzamientos freeleech",
|
||||
"Menu": "Menú",
|
||||
"Mixed": "Mezclado",
|
||||
"DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText": "Descarga primero las primeras y últimas piezas (qBittorrent 4.1.0+)",
|
||||
"DownloadClientQbittorrentSettingsFirstAndLastFirst": "Primeras y últimas primero",
|
||||
"DownloadClientQbittorrentSettingsInitialStateHelpText": "Estado inicial para los torrents añadidos a qBittorrent. Ten en cuenta que Forzar torrents no cumple las restricciones de semilla",
|
||||
"DownloadClientRTorrentSettingsAddStopped": "Añadir detenido",
|
||||
"DownloadClientRTorrentSettingsAddStoppedHelpText": "Permite añadir torrents y magnets a rTorrent en estado detenido. Esto puede romper los archivos magnet.",
|
||||
"DownloadClientQbittorrentSettingsUseSslHelpText": "Usa una conexión segura. Ver en Opciones -> Interfaz web -> 'Usar HTTPS en lugar de HTTP' en qbittorrent.",
|
||||
"DownloadClientRTorrentSettingsDirectoryHelpText": "Ubicación opcional en la que poner las descargas, dejar en blanco para usar la ubicación predeterminada de rTorrent",
|
||||
"DownloadClientSettingsDefaultCategorySubFolderHelpText": "Categoría devuelta por defecto si no existe ninguna categoría mapeada para un lanzamiento. Añadir una categoría específica a {appName} evita conflictos con descargas no relacionadas con {appName}. Usar una categoría es opcional, pero bastante recomendado. Crea un subdirectorio [categoría] en el directorio de salida.",
|
||||
"DownloadClientSettingsAddPaused": "Añadir pausado",
|
||||
"DownloadClientSettingsPriorityItemHelpText": "Prioridad a usar cuando se capturen elementos",
|
||||
"DownloadClientSettingsInitialStateHelpText": "Estado inicial para torrents añadidos a {clientName}",
|
||||
"DownloadClientSettingsUseSslHelpText": "Usa una conexión segura cuando haya una conexión a {clientName}",
|
||||
"DownloadClientTransmissionSettingsDirectoryHelpText": "Ubicación opcional en la que poner las descargas, dejar en blanco para usar la ubicación predeterminada de Transmission",
|
||||
"DownloadClientSettingsUrlBaseHelpText": "Añade un prefijo a la url {clientName}, como {url}",
|
||||
"IndexerBeyondHDSettingsApiKeyHelpText": "Clave API del sitio (encontrada en Mi seguridad => Clave API)",
|
||||
"IndexerBeyondHDSettingsFreeleechOnlyHelpText": "Buscar solo lanzamientos freeleech",
|
||||
"IndexerBeyondHDSettingsLimitedOnlyHelpText": "Buscar solo freeleech (UL limitada)",
|
||||
"IndexerFileListSettingsFreeleechOnlyHelpText": "Buscar solo lanzamientos freeleech",
|
||||
"IndexerFileListSettingsPasskeyHelpText": "Clave de acceso del sitio (esto es la cadena alfanumérica en la url del tracket mostrada en tu cliente de descarga)",
|
||||
"IndexerFileListSettingsUsernameHelpText": "Usuario del sitio",
|
||||
"IndexerHDBitsSettingsMediums": "Medios",
|
||||
"IndexerHDBitsSettingsCodecs": "Códecs",
|
||||
"IndexerHDBitsSettingsFreeleechOnlyHelpText": "Mostrar solo lanzamientos freeleech",
|
||||
"IndexerNebulanceSettingsApiKeyHelpText": "Clave API de Opciones de usuario > Claves API. La clave debe tener permisos de lista y descarga",
|
||||
"IndexerNewznabSettingsVipExpirationHelpText": "Introduce la fecha (yyyy-mm-dd) para Expiración VIP o en blanco, {appName} notificará a una semana de la expiración del VIP",
|
||||
"IndexerOrpheusSettingsApiKeyHelpText": "Clave API del sitio (encontrada en Opciones => Opciones de acceso)",
|
||||
"IndexerPassThePopcornSettingsFreeleechOnlyHelpText": "Buscar solo lanzamientos freeleech",
|
||||
"IndexerSettingsApiPath": "Ruta de API",
|
||||
"IndexerSettingsBaseUrl": "Url base",
|
||||
"IndexerSettingsPackSeedTime": "Tiempo de sembrado de pack",
|
||||
"IndexerSettingsLimitsUnit": "Unidad de límites",
|
||||
"IndexerSettingsQueryLimit": "Límite de petición",
|
||||
"IndexerTorrentSyndikatSettingsApiKeyHelpText": "Clave API del sitio",
|
||||
"ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "Si un torrent es bloqueado por un hash puede no ser rechazado apropiadamente durante Buscar/RSS para algunos indexadores, habilitar esto permitirá que sea rechazado después de que el torrent sea capturado, pero antes es enviado al cliente.",
|
||||
"IndexerSettingsAppsMinimumSeedersHelpText": "Semillas mínimas requeridas por las aplicaciones para que el indexador capture, dejar vacío usa el predeterminado de Sincronización de perfil",
|
||||
"DownloadClientSettingsDefaultCategoryHelpText": "Categoría devuelta por defecto si no existe ninguna categoría mapeada para un lanzamiento. Añadir una categoría específica a {appName} evita conflictos con descargas no relacionadas con {appName}. Usar una categoría es opcional, pero bastante recomendado.",
|
||||
"IndexerSettingsGrabLimitHelpText": "El número de capturas máximas especificadas por la unidad respectiva que {appName} permitirá al sitio",
|
||||
"IndexerSettingsLimitsUnitHelpText": "La unidad o tiempo para límites de recuento por indexador",
|
||||
"IndexerSettingsPackSeedTimeIndexerHelpText": "El tiempo en que un pack de torrent (temporada o discografía) debería ser compartido antes de detenerse, vacío usa el predeterminado de la aplicación",
|
||||
"DownloadClientPneumaticSettingsNzbFolder": "Carpeta de Nzb",
|
||||
"DownloadClientTransmissionSettingsUrlBaseHelpText": "Añade un prefijo a la url rpc de {clientName}, p. ej. {url}, predeterminado a '{defaultUrl}'",
|
||||
"DownloadClientPneumaticSettingsNzbFolderHelpText": "Esta carpeta necesitará ser alcanzable desde XBMC",
|
||||
"DownloadClientPneumaticSettingsStrmFolder": "Carpeta de Strm",
|
||||
"DownloadClientPneumaticSettingsStrmFolderHelpText": "Los archivos .strm en esta carpeta será importados por drone",
|
||||
"DownloadClientQbittorrentSettingsSequentialOrder": "Orden secuencial",
|
||||
"DownloadClientFreeboxSettingsHostHelpText": "Nombre de host o dirección IP de host del Freebox, predeterminado a '{url}' (solo funcionará en la misma red)",
|
||||
"DownloadClientFreeboxSettingsPortHelpText": "Puerto usado para acceder a la interfaz de Freebox, predeterminado a '{port}'",
|
||||
"DownloadClientNzbgetSettingsAddPausedHelpText": "Esta opción requiere al menos NzbGet versión 16.0",
|
||||
"DownloadClientQbittorrentSettingsSequentialOrderHelpText": "Descarga en orden secuencial (qBittorrent 4.1.0+)",
|
||||
"DownloadClientRTorrentSettingsUrlPathHelpText": "Ruta al endpoint de XMLRPC, ver {url}. Esto es usualmente RPC2 o [ruta a ruTorrent]{url2} cuando se usa ruTorrent.",
|
||||
"DownloadClientSettingsDestinationHelpText": "Especifica manualmente el destino de descarga, dejar en blanco para usar el predeterminado",
|
||||
"DownloadClientSettingsInitialState": "Estado inicial",
|
||||
"IndexerBeyondHDSettingsRefundOnly": "Solo devueltos",
|
||||
"IndexerBeyondHDSettingsRefundOnlyHelpText": "Buscar solo devueltos",
|
||||
"IndexerBeyondHDSettingsRewindOnly": "Solo rebobinados",
|
||||
"IndexerBeyondHDSettingsSearchTypesHelpText": "Selecciona los tipos de lanzamientos en los que estás interesado. Si no se selecciona ninguno, se usarán todas las opciones.",
|
||||
"IndexerGazelleGamesSettingsApiKeyHelpText": "Clave API del sitio (encontrada en Opciones => Opciones de acceso)",
|
||||
"IndexerSettingsGrabLimit": "Límite de captura",
|
||||
"IndexerSettingsQueryLimitHelpText": "El número de peticiones máximas especificadas por la unidad respectiva que {appName} permitirá al sitio",
|
||||
"IndexerSettingsSeedTime": "Tiempo de sembrado",
|
||||
"IndexerSettingsCookieHelpText": "Cookie del sitio",
|
||||
"IndexerSettingsFreeleechOnly": "Solo freeleech",
|
||||
"IndexerSettingsVipExpiration": "Expiración del VIP",
|
||||
"XmlRpcPath": "Ruta RPC de XML"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"IndexerProxyStatusCheckSingleClientMessage": "Välityspalvelimet eivät ole käytettävissä virheiden vuoksi: {0}",
|
||||
"IndexerProxyStatusUnavailableHealthCheckMessage": "Välityspalvelimet eivät ole käytettävissä virheiden vuoksi: {indexerProxyNames}",
|
||||
"Logging": "Lokikirjaus",
|
||||
"LogLevel": "Lokikirjauksen laajuus",
|
||||
"MovieIndexScrollTop": "Elokuvakirjasto: vieritä ylös",
|
||||
@@ -51,15 +51,15 @@
|
||||
"SettingsLongDateFormat": "Pitkän päiväyksen esitys",
|
||||
"SettingsShortDateFormat": "Lyhyen päiväyksen esitys",
|
||||
"UnselectAll": "Tyhjennä valinnat",
|
||||
"UpdateCheckStartupTranslocationMessage": "Päivitystä ei voida asentaa, koska käynnistyskansio \"{0}\" sijaitsee \"App Translocation\" -kansiossa.",
|
||||
"UpdateCheckUINotWritableMessage": "Päivityksen asennus ei onnistu, koska käyttäjällä \"{1}\" ei ole kirjoitusoikeutta käyttöliittymäkansioon \"{0}\".",
|
||||
"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}\".",
|
||||
"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ä",
|
||||
"UpdateCheckStartupNotWritableMessage": "Päivitystä ei voida asentaa, koska käyttäjällä \"{1}\" ei ole kirjoitusoikeutta käynnistyskansioon \"{0}\".",
|
||||
"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",
|
||||
@@ -93,9 +93,9 @@
|
||||
"Presets": "Esiasetukset",
|
||||
"Priority": "Painotus",
|
||||
"Protocol": "Protokolla",
|
||||
"ProxyCheckBadRequestMessage": "Välityspalvelintesti epäonnistui. Tilakoodi: {0}.",
|
||||
"ProxyCheckFailedToTestMessage": "Välityspalvelintesti epäonnistui: {0}",
|
||||
"ProxyCheckResolveIpMessage": "Määritetyn välityspalvelimen \"{0}\" IP-osoitteen selvitys epäonnistui.",
|
||||
"ProxyBadRequestHealthCheckMessage": "Välityspalvelintesti epäonnistui. Tilakoodi: {statusCode}.",
|
||||
"ProxyFailedToTestHealthCheckMessage": "Välityspalvelintesti epäonnistui: {url}",
|
||||
"ProxyResolveIpHealthCheckMessage": "Määritetyn välityspalvelimen \"{0}\" 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.",
|
||||
@@ -154,7 +154,7 @@
|
||||
"Disabled": "Ei käytössä",
|
||||
"DownloadClients": "Lataustyökalut",
|
||||
"DownloadClientSettings": "Lataustyökalujen asetukset",
|
||||
"DownloadClientStatusCheckAllClientMessage": "Lataustyökaluja ei ole ongelmien vuoksi käytettävissä",
|
||||
"DownloadClientStatusAllClientHealthCheckMessage": "Lataustyökaluja ei ole ongelmien vuoksi käytettävissä",
|
||||
"Mode": "Tila",
|
||||
"MoreInfo": "Lisätietoja",
|
||||
"SelectAll": "Valitse kaikki",
|
||||
@@ -247,7 +247,7 @@
|
||||
"DeleteBackup": "Poista varmuuskopio",
|
||||
"DeleteBackupMessageText": "Haluatko varmasti poistaa varmuuskopion \"{name}\"?",
|
||||
"DeleteDownloadClient": "Poista lataustyökalu",
|
||||
"DownloadClientStatusCheckSingleClientMessage": "Lataustyökaluja ei ole ongelmien vuoksi käytettävissä: {0}",
|
||||
"DownloadClientStatusSingleClientHealthCheckMessage": "Lataustyökaluja ei ole ongelmien vuoksi käytettävissä: {downloadClientNames}",
|
||||
"EditIndexer": "Muokkaa tietolähdettä",
|
||||
"EnableAutomaticSearch": "Käytä automaattihakua",
|
||||
"EnableInteractiveSearch": "Käytä manuaalihakuun",
|
||||
@@ -275,12 +275,12 @@
|
||||
"IncludeHealthWarningsHelpText": "Sisällytä kuntovaroitukset",
|
||||
"Indexer": "Tietolähde",
|
||||
"IndexerFlags": "Tietolähteen liput",
|
||||
"IndexerLongTermStatusCheckAllClientMessage": "Mikään tietolähde ei ole käytettävissä yli 6 tuntia kestäneiden virheiden vuoksi.",
|
||||
"IndexerLongTermStatusCheckSingleClientMessage": "Tietolähteet eivät ole käytettävissä yli 6 tuntia kestäneiden virheiden vuoksi: {0}",
|
||||
"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",
|
||||
"IndexerProxyStatusCheckAllClientMessage": "Välityspalvelimet eivät ole käytettävissä virheiden vuoksi",
|
||||
"IndexerStatusCheckAllClientMessage": "Tietolähteet eivät ole käytettävissä virheiden vuoksi",
|
||||
"IndexerStatusCheckSingleClientMessage": "Tietolähteet eivät ole käytettävissä virheiden vuoksi: {0}",
|
||||
"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}",
|
||||
"NoChange": "Ei muutosta",
|
||||
"NoLogFiles": "Lokitiedostoja ei ole",
|
||||
"SSLCertPasswordHelpText": "PFX-tiedoston salasana",
|
||||
@@ -317,7 +317,7 @@
|
||||
"IndexerObsoleteCheckMessage": "Tietolähteet ovat poistuneet tai ne ovat muuttuneet: {0}. Poista ja/tai lisää ne {appName}iin uudelleen.",
|
||||
"IndexerProxy": "Tiedonhaun välityspalvelin",
|
||||
"IndexerSettingsSummary": "Määritä useita globaaleita tietolähdeasetuksia, kuten välityspalvelimia.",
|
||||
"IndexerVipCheckExpiringClientMessage": "Tietolähteen VIP-edut erääntyvät pian: {0}",
|
||||
"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",
|
||||
"AddIndexerProxy": "Lisää tiedonhaun välityspalvelin",
|
||||
@@ -330,7 +330,7 @@
|
||||
"IndexerRss": "Tietolähteen RSS",
|
||||
"SearchIndexers": "Etsi tietolähteistä",
|
||||
"AddRemoveOnly": "Ainoastaan lisää/poista",
|
||||
"IndexerVipCheckExpiredClientMessage": "Tietolähteen VIP-edut ovat erääntyneet: {0}",
|
||||
"IndexerVipExpiredHealthCheckMessage": "Tietolähteen VIP-edut ovat erääntyneet: {indexerNames}",
|
||||
"MaintenanceRelease": "Huoltojulkaisu: korjauksia ja muita parannuksia. Lue lisää Githubin muutoshistoriasta.",
|
||||
"Query": "Kysely",
|
||||
"Redirect": "Uudelleenohjaus",
|
||||
@@ -480,7 +480,7 @@
|
||||
"Artist": "Esittäjä",
|
||||
"Author": "Kirjailija",
|
||||
"Book": "Kirja",
|
||||
"UpdateAvailable": "Uusi päivitys on saatavilla",
|
||||
"UpdateAvailableHealthCheckMessage": "Uusi päivitys on saatavilla",
|
||||
"Episode": "Jakso",
|
||||
"Label": "Nimi",
|
||||
"Theme": "Teema",
|
||||
@@ -492,7 +492,7 @@
|
||||
"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ä: {0}",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Ilmoitukset eivät ole ongelmien vuoksi käytettävissä: {notificationNames}",
|
||||
"AuthBasic": "Perus (ponnahdusikkuna)",
|
||||
"AuthForm": "Lomake (kirjautumissivu)",
|
||||
"DisabledForLocalAddresses": "Ei käytössä paikallisille osoitteille",
|
||||
@@ -511,7 +511,7 @@
|
||||
"NoDownloadClientsFound": "Lataustyökaluja ei löytynyt",
|
||||
"CountDownloadClientsSelected": "{count} lataustyökalu(a) on valittu",
|
||||
"EditSelectedDownloadClients": "Muokkaa valittuja lataustyökaluja",
|
||||
"IndexerDownloadClientHealthCheckMessage": "Tietolähteet virheellisillä lataustyökaluilla: {0}.",
|
||||
"IndexerDownloadClientHealthCheckMessage": "Tietolähteet virheellisillä lataustyökaluilla: {indexerNames}.",
|
||||
"AddIndexerProxyImplementation": "Lisää tiedonhaun välityspalvelin - {implementationName}",
|
||||
"EditIndexerProxyImplementation": "Muokkaa tiedonhaun välityspalvelinta - {implementationName}",
|
||||
"EditDownloadClientImplementation": "Muokataan lataustyökalua - {implementationName}",
|
||||
@@ -564,7 +564,7 @@
|
||||
"SearchAllIndexers": "Etsi kaikista tietolähteistä",
|
||||
"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 {0} merkin pituiseksi. Voit tehdä tämän asetuksista tai muokkaamalla asetustiedostoa.",
|
||||
"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ä",
|
||||
@@ -608,5 +608,77 @@
|
||||
"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",
|
||||
"NoApplicationsFound": "Sovelluksia ei löytynyt",
|
||||
"DownloadClientAriaSettingsDirectoryHelpText": "Valinnainen latuasten tallennussijainti. Käytä Aria2-oletusta jättämällä tyhjäksi."
|
||||
"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ä.",
|
||||
"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.",
|
||||
"Menu": "Valikko",
|
||||
"ApplicationSettingsSyncRejectBlocklistedTorrentHashes": "Hylkää estetyt torrent-hajautusarvot kaapattaessa",
|
||||
"IndexerBeyondHDSettingsSearchTypes": "Mitä etsitään",
|
||||
"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.",
|
||||
"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",
|
||||
"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ä",
|
||||
"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.",
|
||||
"IndexerHDBitsSettingsCodecs": "Koodekit",
|
||||
"IndexerHDBitsSettingsCodecsHelpText": "Jos ei määritetty, käytetään kaikkia vaihtoehtoja.",
|
||||
"IndexerHDBitsSettingsMediums": "Mediatyypit",
|
||||
"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}).",
|
||||
"IndexerSettingsCookie": "Eväste",
|
||||
"IndexerSettingsPackSeedTime": "Koosteen 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ä.",
|
||||
"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.",
|
||||
"BlackholeFolderHelpText": "Kansio, jonne {appName} tallentaa {extension}-tiedoston.",
|
||||
"DownloadClientDelugeSettingsUrlBaseHelpText": "Lisää etuliitteen Delugen JSON-URL-osoitteeseen (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}\".",
|
||||
"DownloadClientQbittorrentSettingsFirstAndLastFirst": "Ensimmäinen ja viimeinen ensin",
|
||||
"DownloadClientFreeboxSettingsAppId": "Sovellustunniste",
|
||||
"DownloadClientFreeboxSettingsPortHelpText": "Freebox-liittymän portti. Oletus on \"{port}\".",
|
||||
"DownloadClientPneumaticSettingsNzbFolder": "NZB-kansio",
|
||||
"DownloadClientQbittorrentSettingsSequentialOrder": "Peräkkäinen järjestys",
|
||||
"CustomFilter": "Omat suodattimet",
|
||||
"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+).",
|
||||
"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ä).",
|
||||
"TorrentBlackholeTorrentFolder": "Torrent-kansio",
|
||||
"TorrentBlackholeSaveMagnetFilesExtension": "Tallennettujen magnet-tiedostojen pääte",
|
||||
"TorrentBlackholeSaveMagnetFilesExtensionHelpText": "Magnet-linkeille käytettävä tiedostopääte. Oletus on \".magnet\"."
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"IndexerStatusCheckAllClientMessage": "Tous les indexeurs sont indisponibles en raison d'échecs",
|
||||
"IndexerStatusAllUnavailableHealthCheckMessage": "Tous les indexeurs sont indisponibles en raison d'échecs",
|
||||
"Indexers": "Indexeurs",
|
||||
"Host": "Hôte",
|
||||
"History": "Historique",
|
||||
@@ -11,7 +11,7 @@
|
||||
"Files": "Fichiers",
|
||||
"Events": "Événements",
|
||||
"Edit": "Modifier",
|
||||
"DownloadClientStatusCheckAllClientMessage": "Aucun client de téléchargement n'est disponible en raison d'échecs",
|
||||
"DownloadClientStatusAllClientHealthCheckMessage": "Aucun client de téléchargement n'est disponible en raison d'échecs",
|
||||
"DownloadClients": "Clients de télécharg.",
|
||||
"Dates": "Dates",
|
||||
"Date": "Date",
|
||||
@@ -25,14 +25,14 @@
|
||||
"AppDataLocationHealthCheckMessage": "La mise à jour ne sera pas possible afin empêcher la suppression de AppData lors de la mise à jour",
|
||||
"Analytics": "Statistiques",
|
||||
"All": "Tout",
|
||||
"About": "Tagalog",
|
||||
"IndexerStatusCheckSingleClientMessage": "Indexeurs indisponibles en raison d'échecs : {0}",
|
||||
"DownloadClientStatusCheckSingleClientMessage": "Clients de Téléchargement indisponibles en raison d'échecs : {0}",
|
||||
"About": "À propos",
|
||||
"IndexerStatusUnavailableHealthCheckMessage": "Indexeurs indisponibles en raison d'échecs : {indexerNames}",
|
||||
"DownloadClientStatusSingleClientHealthCheckMessage": "Clients de Téléchargement indisponibles en raison d'échecs : {downloadClientNames}",
|
||||
"SetTags": "Définir des étiquettes",
|
||||
"ReleaseStatus": "Statut de la version",
|
||||
"UpdateCheckUINotWritableMessage": "Impossible d'installer la mise à jour car le dossier d'interface utilisateur '{0}' n'est pas accessible en écriture par l'utilisateur '{1}'.",
|
||||
"UpdateCheckStartupTranslocationMessage": "Impossible d'installer la mise à jour car le dossier de démarrage '{0}' se trouve dans un dossier App Translocation.",
|
||||
"UpdateCheckStartupNotWritableMessage": "Impossible d'installer la mise à jour car le dossier de démarrage '{0}' n'est pas accessible en écriture par l'utilisateur '{1}'.",
|
||||
"UpdateUiNotWritableHealthCheckMessage": "Impossible d'installer la mise à jour car le dossier d'interface utilisateur '{uiFolder}' n'est pas accessible en écriture par l'utilisateur '{userName}'.",
|
||||
"UpdateStartupTranslocationHealthCheckMessage": "Impossible d'installer la mise à jour car le dossier de démarrage '{startupFolder}' se trouve dans un dossier App Translocation.",
|
||||
"UpdateStartupNotWritableHealthCheckMessage": "Impossible d'installer la mise à jour car le dossier de démarrage '{startupFolder}' n'est pas accessible en écriture par l'utilisateur '{userName}'.",
|
||||
"UnselectAll": "Tout désélectionner",
|
||||
"UISettingsSummary": "Date, langue, et perceptions des couleurs",
|
||||
"TagsSettingsSummary": "Voir toutes les étiquettes et comment elles sont utilisées. Les étiquettes inutilisées peuvent être supprimées",
|
||||
@@ -51,9 +51,9 @@
|
||||
"ReleaseBranchCheckOfficialBranchMessage": "La branche {0} n'est pas une branche de version {appName} valide, vous ne recevrez pas de mises à jour",
|
||||
"Refresh": "Rafraîchir",
|
||||
"Queue": "File d'attente",
|
||||
"ProxyCheckResolveIpMessage": "Impossible de résoudre l'adresse IP de l'hôte proxy configuré {0}",
|
||||
"ProxyCheckFailedToTestMessage": "Échec du test du proxy : {0}",
|
||||
"ProxyCheckBadRequestMessage": "Échec du test du proxy. Code d'état : {0}",
|
||||
"ProxyResolveIpHealthCheckMessage": "Impossible de résoudre l'adresse IP de l'hôte proxy configuré {proxyHostName}",
|
||||
"ProxyFailedToTestHealthCheckMessage": "Échec du test du proxy : {url}",
|
||||
"ProxyBadRequestHealthCheckMessage": "Échec du test du proxy. Code d'état : {statusCode}",
|
||||
"Proxy": "Proxy",
|
||||
"Protocol": "Protocole",
|
||||
"Options": "Options",
|
||||
@@ -284,8 +284,8 @@
|
||||
"OnHealthIssueHelpText": "Sur un problème de santé",
|
||||
"AcceptConfirmationModal": "Accepter les modalités d'utilisation",
|
||||
"OpenThisModal": "Ouvrir cette fenêtre modale",
|
||||
"IndexerLongTermStatusCheckSingleClientMessage": "Indexeurs indisponibles en raison de pannes pendant plus de 6 heures : {0}",
|
||||
"IndexerLongTermStatusCheckAllClientMessage": "Tous les indexeurs sont indisponibles en raison d'échecs de plus de 6 heures",
|
||||
"IndexerLongTermStatusUnavailableHealthCheckMessage": "Indexeurs indisponibles en raison de pannes pendant plus de 6 heures : {indexerNames}",
|
||||
"IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Tous les indexeurs sont indisponibles en raison d'échecs de plus de 6 heures",
|
||||
"Yesterday": "Hier",
|
||||
"Tomorrow": "Demain",
|
||||
"Today": "Aujourd'hui",
|
||||
@@ -368,13 +368,13 @@
|
||||
"AppSettingsSummary": "Applications et paramètres pour configurer comment {appName} interagit avec vos programmes PVR",
|
||||
"IndexerTagsHelpText": "Utilisez des étiquettes pour spécifier les proxies d'indexation ou les applications avec lesquelles l'indexeur est synchronisé.",
|
||||
"Notifications": "Notifications",
|
||||
"IndexerVipCheckExpiredClientMessage": "Les avantages VIP de l'indexeur ont expiré : {0}",
|
||||
"IndexerVipExpiredHealthCheckMessage": "Les avantages VIP de l'indexeur ont expiré : {indexerNames}",
|
||||
"IndexerProxy": "Proxy d'indexation",
|
||||
"IndexerSettingsSummary": "Configuration de divers paramètres globaux de l'indexeur, y compris les proxies.",
|
||||
"IndexerProxies": "Proxys d'indexation",
|
||||
"IndexerProxyStatusCheckAllClientMessage": "Tous les proxys sont indisponibles en raison d'échecs",
|
||||
"IndexerProxyStatusCheckSingleClientMessage": "Proxys indisponibles en raison d'échecs : {0}",
|
||||
"IndexerVipCheckExpiringClientMessage": "Les avantages VIP de l'indexeur arrivent bientôt à expiration : {0}",
|
||||
"IndexerProxyStatusAllUnavailableHealthCheckMessage": "Tous les proxys sont indisponibles en raison d'échecs",
|
||||
"IndexerProxyStatusUnavailableHealthCheckMessage": "Proxys indisponibles en raison d'échecs : {indexerProxyNames}",
|
||||
"IndexerVipExpiringHealthCheckMessage": "Les avantages VIP de l'indexeur arrivent bientôt à expiration : {indexerNames}",
|
||||
"NoLinks": "Aucun liens",
|
||||
"Notification": "Notification",
|
||||
"UnableToAddANewIndexerProxyPleaseTryAgain": "Impossible d'ajouter un nouveau proxy d'indexation, veuillez réessayer.",
|
||||
@@ -493,7 +493,7 @@
|
||||
"Track": "Piste",
|
||||
"Year": "Année",
|
||||
"ApplicationURL": "URL de l'application",
|
||||
"ApiKeyValidationHealthCheckMessage": "Veuillez mettre à jour votre clé API pour qu'elle contienne au moins {0} caractères. Vous pouvez le faire via les paramètres ou le fichier de configuration",
|
||||
"ApiKeyValidationHealthCheckMessage": "Veuillez mettre à jour votre clé API pour qu'elle contienne au moins {length} caractères. Vous pouvez le faire via les paramètres ou le fichier de configuration",
|
||||
"ApplicationUrlHelpText": "L'URL externe de cette application, y compris http(s)://, le port ainsi que la base de URL",
|
||||
"ApplyChanges": "Appliquer les modifications",
|
||||
"ApplyTagsHelpTextAdd": "Ajouter : ajoute les étiquettes à la liste de étiquettes existantes",
|
||||
@@ -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",
|
||||
"UpdateAvailable": "Une nouvelle mise à jour est disponible",
|
||||
"UpdateAvailableHealthCheckMessage": "Une nouvelle mise à jour est disponible",
|
||||
"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",
|
||||
@@ -537,7 +537,7 @@
|
||||
"AddIndexerImplementation": "Ajouter un indexeur - {implementationName}",
|
||||
"EditConnectionImplementation": "Modifier la connexion - {implementationName}",
|
||||
"NotificationStatusAllClientHealthCheckMessage": "Toutes les notifications ne sont pas disponibles en raison d'échecs",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Notifications indisponibles en raison d'échecs : {0}",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Notifications indisponibles en raison d'échecs : {notificationNames}",
|
||||
"EditApplicationImplementation": "Ajouter une condition - {implementationName}",
|
||||
"EditIndexerImplementation": "Modifier l'indexeur - {implementationName}",
|
||||
"EditIndexerProxyImplementation": "Modifier un proxy d'indexeur - {implementationName}",
|
||||
@@ -587,7 +587,7 @@
|
||||
"AddDownloadClientImplementation": "Ajouter un client de téléchargement - {implementationName}",
|
||||
"ManageDownloadClients": "Gérer les clients de téléchargement",
|
||||
"AuthenticationRequiredPasswordHelpTextWarning": "Saisir un nouveau mot de passe",
|
||||
"IndexerDownloadClientHealthCheckMessage": "Indexeurs avec des clients de téléchargement invalides : {0].",
|
||||
"IndexerDownloadClientHealthCheckMessage": "Indexeurs avec des clients de téléchargement invalides : {indexerNames}.",
|
||||
"AuthenticationMethod": "Méthode d'authentification",
|
||||
"AuthenticationMethodHelpTextWarning": "Veuillez choisir une méthode d'authentification valide",
|
||||
"ActiveIndexers": "Indexeurs actifs",
|
||||
@@ -733,11 +733,19 @@
|
||||
"DownloadClientFreeboxSettingsAppIdHelpText": "L'ID de l'application donné lors de la création de l'accès à l'API Freebox (c'est-à-dire 'app_id')",
|
||||
"DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText": "Télécharger d'abord le premier et le dernier morceau (qBittorrent 4.1.0+)",
|
||||
"DownloadClientQbittorrentSettingsUseSslHelpText": "Utilisez une connexion sécurisée. Voir Options -> UI Web -> 'Utiliser HTTPS au lieu de HTTP' dans qBittorrent.",
|
||||
"IndexerSettingsRejectBlocklistedTorrentHashesHelpText": "Si un torrent est bloqué par le hachage, il peut ne pas être correctement rejeté pendant le RSS/recherche pour certains indexeurs. L'activation de cette fonction permet de le rejeter après que le torrent a été saisi, mais avant qu'il ne soit envoyé au client.",
|
||||
"TorrentBlackholeSaveMagnetFilesExtensionHelpText": "Extension à utiliser pour les liens magnétiques, la valeur par défaut est '.magnet'",
|
||||
"TorrentBlackholeTorrentFolder": "Dossier Torrent",
|
||||
"UseSsl": "Utiliser SSL",
|
||||
"IndexerSettingsRejectBlocklistedTorrentHashes": "Rejeter les hachages de torrents bloqués lors de la saisie",
|
||||
"DownloadClientRTorrentSettingsDirectoryHelpText": "Emplacement facultatif dans lequel placer les téléchargements. Laisser vide pour utiliser l'emplacement par défaut de rTorrent",
|
||||
"DownloadClientSettingsDefaultCategorySubFolderHelpText": "Catégorie de secours par défaut si aucune catégorie mappée n'existe pour une version. L'ajout d'une catégorie spécifique à {appName} permet d'éviter les conflits avec des téléchargements sans rapport avec {appName}. L'utilisation d'une catégorie est facultative, mais fortement recommandée. Crée un sous-répertoire [catégorie] dans le répertoire de sortie."
|
||||
"DownloadClientSettingsDefaultCategorySubFolderHelpText": "Catégorie de secours par défaut si aucune catégorie mappée n'existe pour une version. L'ajout d'une catégorie spécifique à {appName} permet d'éviter les conflits avec des téléchargements sans rapport avec {appName}. L'utilisation d'une catégorie est facultative, mais fortement recommandée. Crée un sous-répertoire [catégorie] dans le répertoire de sortie.",
|
||||
"ProwlarrDownloadClientsInAppOnlyAlert": "Les clients de téléchargement servent uniquement à effectuer des recherches dans l'application {appName} et ne se synchronisent pas avec les applications. Il n'est pas prévu d'ajouter une telle fonctionnalité.",
|
||||
"Donate": "Donation",
|
||||
"Menu": "Menu",
|
||||
"Mixed": "Mixte",
|
||||
"ApplicationSettingsSyncRejectBlocklistedTorrentHashes": "Synchroniser le rejet des hachages torrent sur liste bloquée lors de la saisie",
|
||||
"ClickToChangeQueryOptions": "Cliquez pour modifier les options de la requête",
|
||||
"ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "Si un torrent est bloqué par le hachage, il peut ne pas être correctement rejeté pendant le RSS/recherche pour certains indexeurs. L'activation de cette fonction permet de le rejeter après que le torrent a été saisi, mais avant qu'il ne soit envoyé au client.",
|
||||
"ProwlarrDownloadClientsAlert": "Si vous avez l'intention d'effectuer des recherches directement dans {appName}, vous devez ajouter les clients de téléchargement. Sinon, vous n'avez pas besoin de les ajouter ici. Pour les recherches à partir de vos applications, les clients de téléchargement qui y sont configurés sont utilisés à la place.",
|
||||
"IndexerMTeamTpSettingsApiKeyHelpText": "Clé API du site (trouvée dans le panneau de configuration utilisateur => Sécurité => Laboratoire)",
|
||||
"IndexerMTeamTpSettingsFreeleechOnlyHelpText": "Rechercher uniquement les versions freeleech"
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"NoLinks": "אין קישורים",
|
||||
"PendingChangesDiscardChanges": "מחק שינויים ועזוב",
|
||||
"ProxyBypassFilterHelpText": "השתמש ב- ',' כמפריד וב- '*.' כתו כללי לתת-דומיינים",
|
||||
"ProxyCheckBadRequestMessage": "נכשל בדיקת ה- proxy. קוד קוד: {0}",
|
||||
"ProxyBadRequestHealthCheckMessage": "נכשל בדיקת ה- proxy. קוד קוד: {statusCode}",
|
||||
"ReleaseStatus": "שחרור סטטוס",
|
||||
"Reload": "לִטעוֹן מִחָדָשׁ",
|
||||
"RemovedFromTaskQueue": "הוסר מתור המשימות",
|
||||
@@ -39,8 +39,8 @@
|
||||
"Added": "נוסף",
|
||||
"Component": "רְכִיב",
|
||||
"Info": "מידע",
|
||||
"IndexerLongTermStatusCheckAllClientMessage": "כל האינדקסים אינם זמינים עקב כשלים במשך יותר מ -6 שעות",
|
||||
"IndexerLongTermStatusCheckSingleClientMessage": "אינדקסים לא זמינים עקב כשלים במשך יותר משש שעות: {0}",
|
||||
"IndexerLongTermStatusAllUnavailableHealthCheckMessage": "כל האינדקסים אינם זמינים עקב כשלים במשך יותר מ -6 שעות",
|
||||
"IndexerLongTermStatusUnavailableHealthCheckMessage": "אינדקסים לא זמינים עקב כשלים במשך יותר משש שעות: {indexerNames}",
|
||||
"Manual": "מדריך ל",
|
||||
"PageSize": "גודל עמוד",
|
||||
"PageSizeHelpText": "מספר הפריטים להצגה בכל עמוד",
|
||||
@@ -80,8 +80,8 @@
|
||||
"Custom": "המותאם אישית",
|
||||
"CustomFilters": "מסננים מותאמים אישית",
|
||||
"Date": "תַאֲרִיך",
|
||||
"DownloadClientStatusCheckAllClientMessage": "כל לקוחות ההורדה אינם זמינים עקב כשלים",
|
||||
"DownloadClientStatusCheckSingleClientMessage": "הורדת לקוחות לא זמינה עקב כשלים: {0}",
|
||||
"DownloadClientStatusAllClientHealthCheckMessage": "כל לקוחות ההורדה אינם זמינים עקב כשלים",
|
||||
"DownloadClientStatusSingleClientHealthCheckMessage": "הורדת לקוחות לא זמינה עקב כשלים: {downloadClientNames}",
|
||||
"Fixed": "תוקן",
|
||||
"FocusSearchBox": "תיבת חיפוש פוקוס",
|
||||
"Folder": "תיקיה",
|
||||
@@ -141,7 +141,7 @@
|
||||
"Time": "זְמַן",
|
||||
"Title": "כותרת",
|
||||
"UnableToLoadHistory": "לא ניתן לטעון את ההיסטוריה",
|
||||
"UpdateCheckStartupNotWritableMessage": "לא ניתן להתקין את העדכון מכיוון שתיקיית ההפעלה '{0}' אינה ניתנת לכתיבה על ידי המשתמש '{1}'.",
|
||||
"UpdateStartupNotWritableHealthCheckMessage": "לא ניתן להתקין את העדכון מכיוון שתיקיית ההפעלה '{startupFolder}' אינה ניתנת לכתיבה על ידי המשתמש '{userName}'.",
|
||||
"AddDownloadClient": "הוסף לקוח הורדות",
|
||||
"Filename": "שם קובץ",
|
||||
"Files": "קבצים",
|
||||
@@ -160,15 +160,15 @@
|
||||
"Hostname": "שם מארח",
|
||||
"IndexerPriority": "עדיפות אינדקס",
|
||||
"IndexerPriorityHelpText": "עדיפות אינדקס מ -1 (הגבוה ביותר) ל -50 (הנמוך ביותר). ברירת מחדל: 25.",
|
||||
"IndexerProxyStatusCheckAllClientMessage": "כל הרשימות אינן זמינות בגלל כשלים",
|
||||
"IndexerProxyStatusCheckSingleClientMessage": "אינדקסים לא זמינים בגלל כשלים: {0}",
|
||||
"IndexerStatusCheckAllClientMessage": "כל האינדקסים אינם זמינים עקב כשלים",
|
||||
"IndexerStatusCheckSingleClientMessage": "אינדקסים לא זמינים בגלל כשלים: {0}",
|
||||
"IndexerProxyStatusAllUnavailableHealthCheckMessage": "כל הרשימות אינן זמינות בגלל כשלים",
|
||||
"IndexerProxyStatusUnavailableHealthCheckMessage": "אינדקסים לא זמינים בגלל כשלים: {indexerProxyNames}",
|
||||
"IndexerStatusAllUnavailableHealthCheckMessage": "כל האינדקסים אינם זמינים עקב כשלים",
|
||||
"IndexerStatusUnavailableHealthCheckMessage": "אינדקסים לא זמינים בגלל כשלים: {indexerNames}",
|
||||
"NoBackupsAreAvailable": "אין גיבויים",
|
||||
"PackageVersion": "גרסת חבילה",
|
||||
"Peers": "עמיתים",
|
||||
"ProxyCheckFailedToTestMessage": "נכשל בדיקת ה- proxy: {0}",
|
||||
"ProxyCheckResolveIpMessage": "פתרון כתובת ה- IP עבור מארח ה- Proxy המוגדר {0} נכשל",
|
||||
"ProxyFailedToTestHealthCheckMessage": "נכשל בדיקת ה- proxy: {url}",
|
||||
"ProxyResolveIpHealthCheckMessage": "פתרון כתובת ה- IP עבור מארח ה- Proxy המוגדר {proxyHostName} נכשל",
|
||||
"ProxyPasswordHelpText": "עליך להזין שם משתמש וסיסמה רק אם נדרשים שם. השאר אותם ריקים אחרת.",
|
||||
"ProxyUsernameHelpText": "עליך להזין שם משתמש וסיסמה רק אם נדרשים שם. השאר אותם ריקים אחרת.",
|
||||
"Reddit": "רדיט",
|
||||
@@ -197,8 +197,8 @@
|
||||
"Tomorrow": "מָחָר",
|
||||
"Torrent": "טורנטים",
|
||||
"UnableToAddANewIndexerProxyPleaseTryAgain": "לא ניתן להוסיף אינדקס חדש, נסה שוב.",
|
||||
"UpdateCheckStartupTranslocationMessage": "לא ניתן להתקין את העדכון מכיוון שתיקיית ההפעלה '{0}' נמצאת בתיקיית טרנסלוקציה של אפליקציות.",
|
||||
"UpdateCheckUINotWritableMessage": "לא ניתן להתקין את העדכון מכיוון שתיקיית ממשק המשתמש '{0}' אינה ניתנת לכתיבה על ידי המשתמש '{1}'.",
|
||||
"UpdateStartupTranslocationHealthCheckMessage": "לא ניתן להתקין את העדכון מכיוון שתיקיית ההפעלה '{startupFolder}' נמצאת בתיקיית טרנסלוקציה של אפליקציות.",
|
||||
"UpdateUiNotWritableHealthCheckMessage": "לא ניתן להתקין את העדכון מכיוון שתיקיית ממשק המשתמש '{uiFolder}' אינה ניתנת לכתיבה על ידי המשתמש '{userName}'.",
|
||||
"Updates": "עדכונים",
|
||||
"UpdateScriptPathHelpText": "נתיב לסקריפט מותאם אישית שלוקח חבילת עדכון שחולצה ומטפל בשארית תהליך העדכון",
|
||||
"Uptime": "זמן עבודה",
|
||||
@@ -386,7 +386,7 @@
|
||||
"DeleteSelectedDownloadClients": "מחק את לקוח ההורדות",
|
||||
"DeleteSelectedIndexersMessageText": "האם אתה בטוח שברצונך למחוק את האינדקס '{0}'?",
|
||||
"DownloadClientPriorityHelpText": "העדיפו עדיפות למספר לקוחות הורדה. Round-Robin משמש ללקוחות עם אותה עדיפות.",
|
||||
"ApiKeyValidationHealthCheckMessage": "עדכן בבקשה את מפתח ה־API שלך כדי שיהיה באורך של לפחות {0} תווים. תוכל לעשות זאת בהגדרות או דרך קובץ הקונפיגורציה.",
|
||||
"ApiKeyValidationHealthCheckMessage": "עדכן בבקשה את מפתח ה־API שלך כדי שיהיה באורך של לפחות {length} תווים. תוכל לעשות זאת בהגדרות או דרך קובץ הקונפיגורציה.",
|
||||
"More": "יותר",
|
||||
"Track": "זֵכֶר",
|
||||
"ApplyTagsHelpTextHowToApplyApplications": "כיצד להחיל תגים על הסרטים שנבחרו",
|
||||
@@ -401,7 +401,7 @@
|
||||
"Album": "אלבום",
|
||||
"Artist": "אמן",
|
||||
"NotificationStatusAllClientHealthCheckMessage": "כל הרשימות אינן זמינות בגלל כשלים",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "רשימות לא זמינות בגלל כשלים: {0}",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "רשימות לא זמינות בגלל כשלים: {notificationNames}",
|
||||
"ResetAPIKeyMessageText": "האם אתה בטוח שברצונך לאפס את מפתח ה- API שלך?",
|
||||
"DisabledForLocalAddresses": "מושבת לכתובות מקומיות",
|
||||
"Publisher": "מוציא לאור",
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"Authentication": "प्रमाणीकरण",
|
||||
"ClientPriority": "ग्राहक प्राथमिकता",
|
||||
"Connections": "सम्बन्ध",
|
||||
"DownloadClientStatusCheckSingleClientMessage": "विफलताओं के कारण अनुपलब्ध ग्राहक डाउनलोड करें: {0}",
|
||||
"DownloadClientStatusSingleClientHealthCheckMessage": "विफलताओं के कारण अनुपलब्ध ग्राहक डाउनलोड करें: {downloadClientNames}",
|
||||
"NoChanges": "कोई बदलाव नहीं",
|
||||
"Yesterday": "बिता कल",
|
||||
"DeleteIndexerProxyMessageText": "क्या आप वाकई '{0}' टैग हटाना चाहते हैं?",
|
||||
@@ -104,8 +104,8 @@
|
||||
"DownloadClientSettings": "क्लाइंट सेटिंग्स डाउनलोड करें",
|
||||
"EventType": "घटना प्रकार",
|
||||
"Exception": "अपवाद",
|
||||
"IndexerStatusCheckAllClientMessage": "विफलताओं के कारण सभी अनुक्रमणिका अनुपलब्ध हैं",
|
||||
"IndexerStatusCheckSingleClientMessage": "अनुक्रमणिका विफलताओं के कारण अनुपलब्ध: {0}",
|
||||
"IndexerStatusAllUnavailableHealthCheckMessage": "विफलताओं के कारण सभी अनुक्रमणिका अनुपलब्ध हैं",
|
||||
"IndexerStatusUnavailableHealthCheckMessage": "अनुक्रमणिका विफलताओं के कारण अनुपलब्ध: {indexerNames}",
|
||||
"Info": "जानकारी",
|
||||
"Language": "भाषा: हिन्दी",
|
||||
"UnableToAddANewDownloadClientPleaseTryAgain": "नया डाउनलोड क्लाइंट जोड़ने में असमर्थ, कृपया पुनः प्रयास करें।",
|
||||
@@ -133,9 +133,9 @@
|
||||
"NoLinks": "कोई लिंक नहीं",
|
||||
"BindAddress": "बाँध का पता",
|
||||
"Branch": "डाली",
|
||||
"ProxyCheckBadRequestMessage": "प्रॉक्सी का परीक्षण करने में विफल। स्थिति कोड: {0}",
|
||||
"ProxyCheckFailedToTestMessage": "प्रॉक्सी का परीक्षण करने में विफल: {0}",
|
||||
"ProxyCheckResolveIpMessage": "कॉन्फ़िगर प्रॉक्सी होस्ट {0} के लिए आईपी एड्रेस को हल करने में विफल",
|
||||
"ProxyBadRequestHealthCheckMessage": "प्रॉक्सी का परीक्षण करने में विफल। स्थिति कोड: {statusCode}",
|
||||
"ProxyFailedToTestHealthCheckMessage": "प्रॉक्सी का परीक्षण करने में विफल: {url}",
|
||||
"ProxyResolveIpHealthCheckMessage": "कॉन्फ़िगर प्रॉक्सी होस्ट {proxyHostName} के लिए आईपी एड्रेस को हल करने में विफल",
|
||||
"ProxyPasswordHelpText": "यदि आवश्यक हो तो आपको केवल एक उपयोगकर्ता नाम और पासवर्ड दर्ज करना होगा। उन्हें खाली छोड़ दें अन्यथा।",
|
||||
"ProxyType": "प्रॉक्सी प्रकार",
|
||||
"ProxyUsernameHelpText": "यदि आवश्यक हो तो आपको केवल एक उपयोगकर्ता नाम और पासवर्ड दर्ज करना होगा। उन्हें खाली छोड़ दें अन्यथा।",
|
||||
@@ -170,7 +170,7 @@
|
||||
"UnsavedChanges": "बिना बदलाव किए",
|
||||
"UnselectAll": "सभी का चयन रद्द",
|
||||
"UpdateAutomaticallyHelpText": "अपडेट को स्वचालित रूप से डाउनलोड और इंस्टॉल करें। आप अभी भी सिस्टम से अपडेट कर पाएंगे: अपडेट",
|
||||
"UpdateCheckUINotWritableMessage": "अद्यतन स्थापित नहीं कर सकता क्योंकि UI फ़ोल्डर '{0}' उपयोगकर्ता '{1}' द्वारा लिखने योग्य नहीं है।",
|
||||
"UpdateUiNotWritableHealthCheckMessage": "अद्यतन स्थापित नहीं कर सकता क्योंकि UI फ़ोल्डर '{uiFolder}' उपयोगकर्ता '{userName}' द्वारा लिखने योग्य नहीं है।",
|
||||
"Uptime": "अपटाइम",
|
||||
"URLBase": "URL बेस",
|
||||
"YesCancel": "हाँ, रद्द करें",
|
||||
@@ -234,7 +234,7 @@
|
||||
"CloneProfile": "क्लोन प्रोफ़ाइल",
|
||||
"Close": "बंद करे",
|
||||
"CloseCurrentModal": "वर्तमान मोडल को बंद करें",
|
||||
"DownloadClientStatusCheckAllClientMessage": "सभी डाउनलोड क्लाइंट विफलताओं के कारण अनुपलब्ध हैं",
|
||||
"DownloadClientStatusAllClientHealthCheckMessage": "सभी डाउनलोड क्लाइंट विफलताओं के कारण अनुपलब्ध हैं",
|
||||
"Edit": "संपादित करें",
|
||||
"EnableAutomaticSearch": "स्वचालित खोज सक्षम करें",
|
||||
"EnableAutomaticSearchHelpText": "यूआई के माध्यम से या रेडर द्वारा स्वचालित खोज किए जाने पर उपयोग किया जाएगा",
|
||||
@@ -268,12 +268,12 @@
|
||||
"Hostname": "होस्ट का नाम",
|
||||
"IncludeHealthWarningsHelpText": "स्वास्थ्य चेतावनी शामिल करें",
|
||||
"IndexerFlags": "इंडेक्स फ्लैग",
|
||||
"IndexerLongTermStatusCheckAllClientMessage": "6 घंटे से अधिक समय तक विफलताओं के कारण सभी सूचकांक अनुपलब्ध हैं",
|
||||
"IndexerLongTermStatusCheckSingleClientMessage": "6 घंटे से अधिक समय तक विफलताओं के कारण सूचकांक उपलब्ध नहीं: {0}",
|
||||
"IndexerLongTermStatusAllUnavailableHealthCheckMessage": "6 घंटे से अधिक समय तक विफलताओं के कारण सभी सूचकांक अनुपलब्ध हैं",
|
||||
"IndexerLongTermStatusUnavailableHealthCheckMessage": "6 घंटे से अधिक समय तक विफलताओं के कारण सूचकांक उपलब्ध नहीं: {indexerNames}",
|
||||
"IndexerPriority": "सूचकांक प्राथमिकता",
|
||||
"IndexerPriorityHelpText": "इंडेक्सर प्राथमिकता 1 (उच्चतम) से 50 (सबसे कम)। डिफ़ॉल्ट: 25",
|
||||
"IndexerProxyStatusCheckAllClientMessage": "सभी सूचियाँ विफल होने के कारण अनुपलब्ध हैं",
|
||||
"IndexerProxyStatusCheckSingleClientMessage": "अनुक्रमणिका विफलताओं के कारण अनुपलब्ध: {0}",
|
||||
"IndexerProxyStatusAllUnavailableHealthCheckMessage": "सभी सूचियाँ विफल होने के कारण अनुपलब्ध हैं",
|
||||
"IndexerProxyStatusUnavailableHealthCheckMessage": "अनुक्रमणिका विफलताओं के कारण अनुपलब्ध: {indexerProxyNames}",
|
||||
"InteractiveSearch": "इंटरएक्टिव खोज",
|
||||
"Interval": "मध्यान्तर",
|
||||
"KeyboardShortcuts": "कुंजीपटल अल्प मार्ग",
|
||||
@@ -297,8 +297,8 @@
|
||||
"DownloadClientsLoadError": "डाउनलोड क्लाइंट लोड करने में असमर्थ",
|
||||
"UnableToLoadGeneralSettings": "सामान्य सेटिंग्स लोड करने में असमर्थ",
|
||||
"UnableToLoadNotifications": "सूचनाएं लोड करने में असमर्थ",
|
||||
"UpdateCheckStartupNotWritableMessage": "अपडेट स्थापित नहीं किया जा सकता क्योंकि स्टार्टअप फ़ोल्डर '{0}' उपयोगकर्ता '{1}' द्वारा लिखने योग्य नहीं है।",
|
||||
"UpdateCheckStartupTranslocationMessage": "अपडेट स्थापित नहीं किया जा सकता क्योंकि स्टार्टअप फ़ोल्डर '{0}' ऐप ट्रांसलेशन फ़ोल्डर में है।",
|
||||
"UpdateStartupNotWritableHealthCheckMessage": "अपडेट स्थापित नहीं किया जा सकता क्योंकि स्टार्टअप फ़ोल्डर '{startupFolder}' उपयोगकर्ता '{userName}' द्वारा लिखने योग्य नहीं है।",
|
||||
"UpdateStartupTranslocationHealthCheckMessage": "अपडेट स्थापित नहीं किया जा सकता क्योंकि स्टार्टअप फ़ोल्डर '{startupFolder}' ऐप ट्रांसलेशन फ़ोल्डर में है।",
|
||||
"NoUpdatesAreAvailable": "कोई अद्यतन उपलब्ध नहीं हैं",
|
||||
"OAuthPopupMessage": "आपके ब्राउज़र द्वारा पॉप-अप्स को ब्लॉक किया जा रहा है",
|
||||
"OnHealthIssueHelpText": "स्वास्थ्य के मुद्दे पर",
|
||||
@@ -347,10 +347,12 @@
|
||||
"minutes": "मिनट",
|
||||
"DeleteAppProfileMessageText": "क्या आप वाकई गुणवत्ता प्रोफ़ाइल {0} को हटाना चाहते हैं",
|
||||
"NotificationStatusAllClientHealthCheckMessage": "सभी सूचियाँ विफल होने के कारण अनुपलब्ध हैं",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "विफलताओं के कारण अनुपलब्ध सूची: {0}",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "विफलताओं के कारण अनुपलब्ध सूची: {notificationNames}",
|
||||
"AuthBasic": "बेसिक (ब्राउज़र पॉपअप)",
|
||||
"AuthForm": "प्रपत्र (लॉग इन पेज)",
|
||||
"DisabledForLocalAddresses": "स्थानीय पते के लिए अक्षम",
|
||||
"None": "कोई नहीं",
|
||||
"ResetAPIKeyMessageText": "क्या आप वाकई अपनी API कुंजी को रीसेट करना चाहते हैं?"
|
||||
"ResetAPIKeyMessageText": "क्या आप वाकई अपनी API कुंजी को रीसेट करना चाहते हैं?",
|
||||
"IndexerHDBitsSettingsMediums": "मध्यम",
|
||||
"CustomFilter": "कस्टम फ़िल्टर"
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
"Enable": "Aktiválás",
|
||||
"EditIndexer": "Indexer Szerkesztése",
|
||||
"Edit": "Szerkeszt",
|
||||
"DownloadClientStatusCheckSingleClientMessage": "Letöltőkliens hiba miatt nem elérhető: {0}",
|
||||
"DownloadClientStatusCheckAllClientMessage": "Az összes letöltőkliens elérhetetlen, hiba miatt",
|
||||
"DownloadClientStatusSingleClientHealthCheckMessage": "Letöltőkliens hiba miatt nem elérhető: {downloadClientNames}",
|
||||
"DownloadClientStatusAllClientHealthCheckMessage": "Az összes letöltőkliens elérhetetlen, hiba miatt",
|
||||
"DownloadClientsSettingsSummary": "Letöltőkliens konfigurációja a {appName} felhasználói felület keresésbe történő integráláshoz",
|
||||
"DownloadClientSettings": "Letöltőkliens Beállítások",
|
||||
"DownloadClients": "Letöltő kliensek",
|
||||
@@ -160,9 +160,9 @@
|
||||
"ProxyUsernameHelpText": "Csak akkor kell megadnia egy felhasználónevet és jelszót, ha szükséges. Ellenkező esetben hagyja üresen.",
|
||||
"ProxyType": "Proxy típus",
|
||||
"ProxyPasswordHelpText": "Csak akkor kell megadnia egy felhasználónevet és jelszót, ha szükséges. Ellenkező esetben hagyja üresen.",
|
||||
"ProxyCheckResolveIpMessage": "Nem sikerült megoldani a konfigurált proxykiszolgáló IP-címét {0}",
|
||||
"ProxyCheckFailedToTestMessage": "Proxy tesztelése sikertelen: {0}",
|
||||
"ProxyCheckBadRequestMessage": "Proxy tesztelése sikertelen. Állapotkód: {0}",
|
||||
"ProxyResolveIpHealthCheckMessage": "Nem sikerült megoldani a konfigurált proxykiszolgáló IP-címét {proxyHostName}",
|
||||
"ProxyFailedToTestHealthCheckMessage": "Proxy tesztelése sikertelen: {url}",
|
||||
"ProxyBadRequestHealthCheckMessage": "Proxy tesztelése sikertelen. Állapotkód: {statusCode}",
|
||||
"ProxyBypassFilterHelpText": "Használja a ',' jelet elválasztóként és a '*' jelet. helyettesítő karakterként az aldomainekhez",
|
||||
"Proxy": "Proxy",
|
||||
"Protocol": "Protokoll",
|
||||
@@ -215,7 +215,7 @@
|
||||
"Interval": "Intervallum",
|
||||
"InteractiveSearch": "Interaktív Keresés",
|
||||
"Info": "Infó",
|
||||
"IndexerStatusCheckSingleClientMessage": "Indexerek elérhetetlenek a következő hiba miatt: {0}",
|
||||
"IndexerStatusUnavailableHealthCheckMessage": "Indexerek elérhetetlenek a következő hiba miatt: {indexerNames}",
|
||||
"Hostname": "Hosztnév",
|
||||
"Host": "Hoszt",
|
||||
"Grabbed": "Megragadta",
|
||||
@@ -249,7 +249,7 @@
|
||||
"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",
|
||||
"IndexerStatusCheckAllClientMessage": "Az összes indexer elérhetetlen hiba miatt",
|
||||
"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.",
|
||||
"IndexerPriority": "Indexer Prioritása",
|
||||
@@ -271,9 +271,9 @@
|
||||
"UpdateScriptPathHelpText": "Keresse meg az egyéni parancsfájl elérési útját, amely kibontott frissítési csomagot vesz fel, és kezeli a frissítési folyamat fennmaradó részét",
|
||||
"Updates": "Frissítések",
|
||||
"UpdateMechanismHelpText": "Használja a {appName} beépített frissítőjét vagy szkriptjét",
|
||||
"UpdateCheckUINotWritableMessage": "Nem lehet telepíteni a frissítést, mert a(z) „{0}” felhasználói felület mappát nem írhatja a „{1}” felhasználó.",
|
||||
"UpdateCheckStartupTranslocationMessage": "Nem lehet telepíteni a frissítést, mert a (z) „{0}” indítási mappa az Alkalmazások Transzlokációs mappájában található.",
|
||||
"UpdateCheckStartupNotWritableMessage": "A frissítés nem telepíthető, mert a (z) „{0}” indítási mappát a „{1}” felhasználó nem írhatja.",
|
||||
"UpdateUiNotWritableHealthCheckMessage": "Nem lehet telepíteni a frissítést, mert a(z) „{uiFolder}” felhasználói felület mappát nem írhatja a „{userName}” felhasználó.",
|
||||
"UpdateStartupTranslocationHealthCheckMessage": "Nem lehet telepíteni a frissítést, mert a (z) „{startupFolder}” indítási mappa az Alkalmazások Transzlokációs mappájában található.",
|
||||
"UpdateStartupNotWritableHealthCheckMessage": "A frissítés nem telepíthető, mert a (z) „{startupFolder}” indítási mappát a „{userName}” felhasználó nem írhatja.",
|
||||
"UpdateAutomaticallyHelpText": "A frissítések automatikus letöltése és telepítése. A Rendszer: Frissítések alkalmazásból továbbra is telepíteni tudja",
|
||||
"UnselectAll": "Minden kijelölés megszüntetése",
|
||||
"UnsavedChanges": "Nem mentett változások",
|
||||
@@ -284,8 +284,8 @@
|
||||
"ShowSearch": "Keresés mutatása",
|
||||
"SetTags": "Címkék beállítása",
|
||||
"NotificationTriggers": "Értesítési triggerek",
|
||||
"IndexerLongTermStatusCheckSingleClientMessage": "Az összes indexer elérhetetlen több mint 6 órája, meghibásodás miatt: {0}",
|
||||
"IndexerLongTermStatusCheckAllClientMessage": "Az összes indexer elérhetetlen több mint 6 órája, meghibásodás miatt",
|
||||
"IndexerLongTermStatusUnavailableHealthCheckMessage": "Az összes indexer elérhetetlen több mint 6 órája, meghibásodás miatt: {indexerNames}",
|
||||
"IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Az összes indexer elérhetetlen több mint 6 órája, meghibásodás miatt",
|
||||
"SettingsLogSql": "SQL naplózás",
|
||||
"IndexerRss": "Indexer Rss",
|
||||
"IndexerAuth": "Indexelő auth",
|
||||
@@ -366,16 +366,16 @@
|
||||
"DeleteIndexerProxy": "Indexer Proxy törlése",
|
||||
"DeleteIndexerProxyMessageText": "Biztosan törlöd a(z) „{0}” proxyt?",
|
||||
"IndexerProxies": "Indexer Proxy(k)",
|
||||
"IndexerProxyStatusCheckAllClientMessage": "Az összes Proxy elérhetetlen, hiba miatt",
|
||||
"IndexerProxyStatusCheckSingleClientMessage": "Proxyk elérhetetlenek az alábbi hibák miatt: {0}",
|
||||
"IndexerProxyStatusAllUnavailableHealthCheckMessage": "Az összes Proxy elérhetetlen, hiba miatt",
|
||||
"IndexerProxyStatusUnavailableHealthCheckMessage": "Proxyk elérhetetlenek az alábbi hibák miatt: {indexerProxyNames}",
|
||||
"IndexerTagsHelpText": "Címkék segítségével megadhatod az indexer proxykat amelyekkel az indexer szinkronizálva van, vagy egyszerűen az indexelők rendszerezéséhez.",
|
||||
"UnableToLoadIndexerProxies": "Nem lehet betölteni az Indexer Proxyt",
|
||||
"AddIndexerProxy": "Indexer Proxy hozzáadása",
|
||||
"IndexerSettingsSummary": "Konfigurálja a különböző globális indexer beállításokat, beleértve a proxykat is.",
|
||||
"Notifications": "Értesítések",
|
||||
"UnableToAddANewIndexerProxyPleaseTryAgain": "Nem lehet új Indexer Proxyt hozzáadni, próbálja újra.",
|
||||
"IndexerVipCheckExpiredClientMessage": "Az Indexer VIP előnyei lejártak: {0}",
|
||||
"IndexerVipCheckExpiringClientMessage": "Az Indexer VIP előnyei hamarosan lejárnak: {0}",
|
||||
"IndexerVipExpiredHealthCheckMessage": "Az Indexer VIP előnyei lejártak: {indexerNames}",
|
||||
"IndexerVipExpiringHealthCheckMessage": "Az Indexer VIP előnyei hamarosan lejárnak: {indexerNames}",
|
||||
"IndexerProxy": "Indexelő Proxy",
|
||||
"NoLinks": "Nincsenek Linkek",
|
||||
"Notification": "Értesítés",
|
||||
@@ -476,12 +476,12 @@
|
||||
"EditSelectedDownloadClients": "Kijelölt letöltési kliensek szerkesztése",
|
||||
"Label": "Címke",
|
||||
"SelectIndexers": "Indexelők keresése",
|
||||
"ApiKeyValidationHealthCheckMessage": "Kérlek frissítsd az API kulcsot, ami legalább {0} karakter hosszú. Ezt megteheted a Beállításokban, vagy a config file-ban",
|
||||
"ApiKeyValidationHealthCheckMessage": "Kérlek frissítsd az API kulcsot, ami legalább {length} karakter hosszú. Ezt megteheted a Beállításokban, vagy a config file-ban",
|
||||
"Episode": "Epizód",
|
||||
"Genre": "Műfajok",
|
||||
"Theme": "Téma",
|
||||
"Track": "Dal",
|
||||
"UpdateAvailable": "Új frissítés elérhető",
|
||||
"UpdateAvailableHealthCheckMessage": "Új frissítés elérhető",
|
||||
"Year": "Év",
|
||||
"Book": "Könyv",
|
||||
"Season": "Évad",
|
||||
@@ -496,7 +496,7 @@
|
||||
"minutes": "percek",
|
||||
"AddConnection": "Csatlakozás hozzáadása",
|
||||
"NotificationStatusAllClientHealthCheckMessage": "Az összes értesítés nem érhető el hibák miatt",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Az alkalmazás nem áll rendelkezésre az alábbi hibák miatt: {0}",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Az alkalmazás nem áll rendelkezésre az alábbi hibák miatt: {notificationNames}",
|
||||
"AuthBasic": "Alap (böngésző előugró ablak)",
|
||||
"AuthForm": "Űrlapok (bejelentkezési oldal)",
|
||||
"DisabledForLocalAddresses": "Helyi címeknél letiltva",
|
||||
@@ -546,7 +546,29 @@
|
||||
"DeleteSelectedApplicationsMessageText": "Biztosan törölni szeretne {count} kiválasztott importlistát?",
|
||||
"CountApplicationsSelected": "{count} Gyűjtemény(ek) kiválasztva",
|
||||
"ManageClients": "Ügyfelek kezelése",
|
||||
"IndexerDownloadClientHealthCheckMessage": "Indexelők érvénytelen letöltési kliensekkel: {0}.",
|
||||
"IndexerDownloadClientHealthCheckMessage": "Indexelők érvénytelen letöltési kliensekkel: {indexerNames}.",
|
||||
"PasswordConfirmation": "Jelszó megerősítése",
|
||||
"SecretToken": "Titkos token"
|
||||
"SecretToken": "Titkos token",
|
||||
"UseSsl": "SSL használata",
|
||||
"Donate": "Adományoz",
|
||||
"IndexerBeyondHDSettingsSearchTypes": "Keresés típusa",
|
||||
"Mixed": "Mixed",
|
||||
"TorrentBlackholeSaveMagnetFiles": "Magnet fájlok mentése",
|
||||
"DownloadClientSettingsInitialState": "Kezdeti állapot",
|
||||
"DownloadClientSettingsInitialStateHelpText": "A torrentek kezdeti állapota hozzáadva a következőhöz {clientName}",
|
||||
"DownloadClientTransmissionSettingsDirectoryHelpText": "Választható hely a letöltések elhelyezéséhez, hagyja üresen az alapértelmezett Aria2 hely használatához",
|
||||
"IndexerHDBitsSettingsCodecs": "Kodek",
|
||||
"IndexerHDBitsSettingsMediums": "Közepes",
|
||||
"BlackholeFolderHelpText": "Mappa, amelyben az {appName} tárolja az {extension} fájlt",
|
||||
"Destination": "Rendeltetési hely",
|
||||
"Directory": "Könyvtár",
|
||||
"DownloadClientFloodSettingsAdditionalTags": "További címkék",
|
||||
"DownloadClientFloodSettingsAdditionalTagsHelpText": "Hozzáadja a média tulajdonságait címkékként. A tippek példák.",
|
||||
"TorrentBlackholeSaveMagnetFilesExtension": "Magnet Files kiterjesztés mentése",
|
||||
"TorrentBlackholeTorrentFolder": "Torrent mappa",
|
||||
"DownloadClientDelugeSettingsUrlBaseHelpText": "Előtagot ad a deluge json URL-hez, lásd: {url}",
|
||||
"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"
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user