mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2026-03-05 13:40:08 -05:00
Compare commits
31 Commits
v1.23.0.46
...
v1.24.3.47
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17e1a72baf | ||
|
|
b454ded00a | ||
|
|
d4512393e2 | ||
|
|
97d1384726 | ||
|
|
ba002a7a4a | ||
|
|
349efab7a8 | ||
|
|
af9a6f42db | ||
|
|
6b20fa8abd | ||
|
|
029ad3903f | ||
|
|
a23d66930b | ||
|
|
710ab7ae09 | ||
|
|
434b07ae64 | ||
|
|
eee8c95ca6 | ||
|
|
1f5c514011 | ||
|
|
66d722e097 | ||
|
|
39befe5aa4 | ||
|
|
ab043e87dc | ||
|
|
58ae9c0a13 | ||
|
|
44c446943c | ||
|
|
8301b669fe | ||
|
|
6fa0b79c67 | ||
|
|
32d23d6636 | ||
|
|
b31b695887 | ||
|
|
33de32b138 | ||
|
|
753b53a529 | ||
|
|
123535b9a5 | ||
|
|
7a5fa452f0 | ||
|
|
281e712542 | ||
|
|
c2c34ecf53 | ||
|
|
615193617c | ||
|
|
1b58d50b6d |
@@ -1,7 +1,7 @@
|
||||
# Prowlarr
|
||||
|
||||
[](https://dev.azure.com/Prowlarr/Prowlarr/_build/latest?definitionId=1&branchName=develop)
|
||||
[](https://translate.servarr.com/engage/prowlarr/?utm_source=widget)
|
||||
[](https://translate.servarr.com/engage/servarr/?utm_source=widget)
|
||||
[](https://wiki.servarr.com/prowlarr/installation/docker)
|
||||

|
||||
[](#backers)
|
||||
|
||||
@@ -9,7 +9,7 @@ variables:
|
||||
testsFolder: './_tests'
|
||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||
majorVersion: '1.23.0'
|
||||
majorVersion: '1.24.3'
|
||||
minorVersion: $[counter('minorVersion', 1)]
|
||||
prowlarrVersion: '$(majorVersion).$(minorVersion)'
|
||||
buildName: '$(Build.SourceBranchName).$(prowlarrVersion)'
|
||||
|
||||
@@ -7,7 +7,7 @@ import { icons } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import IndexerSearchInputConnector from './IndexerSearchInputConnector';
|
||||
import KeyboardShortcutsModal from './KeyboardShortcutsModal';
|
||||
import PageHeaderActionsMenuConnector from './PageHeaderActionsMenuConnector';
|
||||
import PageHeaderActionsMenu from './PageHeaderActionsMenu';
|
||||
import styles from './PageHeader.css';
|
||||
|
||||
class PageHeader extends Component {
|
||||
@@ -87,7 +87,8 @@ class PageHeader extends Component {
|
||||
to="https://translate.servarr.com/projects/servarr/prowlarr/"
|
||||
size={24}
|
||||
/>
|
||||
<PageHeaderActionsMenuConnector
|
||||
|
||||
<PageHeaderActionsMenu
|
||||
onKeyboardShortcutsPress={this.onOpenKeyboardShortcutsModal}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Icon from 'Components/Icon';
|
||||
import Menu from 'Components/Menu/Menu';
|
||||
import MenuButton from 'Components/Menu/MenuButton';
|
||||
import MenuContent from 'Components/Menu/MenuContent';
|
||||
import MenuItem from 'Components/Menu/MenuItem';
|
||||
import MenuItemSeparator from 'Components/Menu/MenuItemSeparator';
|
||||
import { align, icons, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './PageHeaderActionsMenu.css';
|
||||
|
||||
function PageHeaderActionsMenu(props) {
|
||||
const {
|
||||
formsAuth,
|
||||
onKeyboardShortcutsPress,
|
||||
onRestartPress,
|
||||
onShutdownPress
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Menu alignMenu={align.RIGHT}>
|
||||
<MenuButton className={styles.menuButton} aria-label="Menu Button">
|
||||
<Icon
|
||||
name={icons.INTERACTIVE}
|
||||
title={translate('Menu')}
|
||||
/>
|
||||
</MenuButton>
|
||||
|
||||
<MenuContent>
|
||||
<MenuItem onPress={onKeyboardShortcutsPress}>
|
||||
<Icon
|
||||
className={styles.itemIcon}
|
||||
name={icons.KEYBOARD}
|
||||
/>
|
||||
{translate('KeyboardShortcuts')}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItemSeparator />
|
||||
|
||||
<MenuItem onPress={onRestartPress}>
|
||||
<Icon
|
||||
className={styles.itemIcon}
|
||||
name={icons.RESTART}
|
||||
/>
|
||||
{translate('Restart')}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onPress={onShutdownPress}>
|
||||
<Icon
|
||||
className={styles.itemIcon}
|
||||
name={icons.SHUTDOWN}
|
||||
kind={kinds.DANGER}
|
||||
/>
|
||||
{translate('Shutdown')}
|
||||
</MenuItem>
|
||||
|
||||
{
|
||||
formsAuth &&
|
||||
<div className={styles.separator} />
|
||||
}
|
||||
|
||||
{
|
||||
formsAuth &&
|
||||
<MenuItem
|
||||
to={`${window.Prowlarr.urlBase}/logout`}
|
||||
noRouter={true}
|
||||
>
|
||||
<Icon
|
||||
className={styles.itemIcon}
|
||||
name={icons.LOGOUT}
|
||||
/>
|
||||
Logout
|
||||
</MenuItem>
|
||||
}
|
||||
</MenuContent>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
PageHeaderActionsMenu.propTypes = {
|
||||
formsAuth: PropTypes.bool.isRequired,
|
||||
onKeyboardShortcutsPress: PropTypes.func.isRequired,
|
||||
onRestartPress: PropTypes.func.isRequired,
|
||||
onShutdownPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default PageHeaderActionsMenu;
|
||||
@@ -0,0 +1,90 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import AppState from 'App/State/AppState';
|
||||
import Icon from 'Components/Icon';
|
||||
import Menu from 'Components/Menu/Menu';
|
||||
import MenuButton from 'Components/Menu/MenuButton';
|
||||
import MenuContent from 'Components/Menu/MenuContent';
|
||||
import MenuItem from 'Components/Menu/MenuItem';
|
||||
import MenuItemSeparator from 'Components/Menu/MenuItemSeparator';
|
||||
import { align, icons, kinds } from 'Helpers/Props';
|
||||
import { restart, shutdown } from 'Store/Actions/systemActions';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './PageHeaderActionsMenu.css';
|
||||
|
||||
interface PageHeaderActionsMenuProps {
|
||||
onKeyboardShortcutsPress(): void;
|
||||
}
|
||||
|
||||
function PageHeaderActionsMenu(props: PageHeaderActionsMenuProps) {
|
||||
const { onKeyboardShortcutsPress } = props;
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { authentication, isDocker } = useSelector(
|
||||
(state: AppState) => state.system.status.item
|
||||
);
|
||||
|
||||
const formsAuth = authentication === 'forms';
|
||||
|
||||
const handleRestartPress = useCallback(() => {
|
||||
dispatch(restart());
|
||||
}, [dispatch]);
|
||||
|
||||
const handleShutdownPress = useCallback(() => {
|
||||
dispatch(shutdown());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Menu alignMenu={align.RIGHT}>
|
||||
<MenuButton className={styles.menuButton} aria-label="Menu Button">
|
||||
<Icon name={icons.INTERACTIVE} title={translate('Menu')} />
|
||||
</MenuButton>
|
||||
|
||||
<MenuContent>
|
||||
<MenuItem onPress={onKeyboardShortcutsPress}>
|
||||
<Icon className={styles.itemIcon} name={icons.KEYBOARD} />
|
||||
{translate('KeyboardShortcuts')}
|
||||
</MenuItem>
|
||||
|
||||
{isDocker ? null : (
|
||||
<>
|
||||
<MenuItemSeparator />
|
||||
|
||||
<MenuItem onPress={handleRestartPress}>
|
||||
<Icon className={styles.itemIcon} name={icons.RESTART} />
|
||||
{translate('Restart')}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onPress={handleShutdownPress}>
|
||||
<Icon
|
||||
className={styles.itemIcon}
|
||||
name={icons.SHUTDOWN}
|
||||
kind={kinds.DANGER}
|
||||
/>
|
||||
{translate('Shutdown')}
|
||||
</MenuItem>
|
||||
</>
|
||||
)}
|
||||
|
||||
{formsAuth ? (
|
||||
<>
|
||||
<MenuItemSeparator />
|
||||
|
||||
<MenuItem
|
||||
to={`${window.Prowlarr.urlBase}/logout`}
|
||||
noRouter={true}
|
||||
>
|
||||
<Icon className={styles.itemIcon} name={icons.LOGOUT} />
|
||||
{translate('Logout')}
|
||||
</MenuItem>
|
||||
</>
|
||||
) : null}
|
||||
</MenuContent>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PageHeaderActionsMenu;
|
||||
@@ -1,56 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { restart, shutdown } from 'Store/Actions/systemActions';
|
||||
import PageHeaderActionsMenu from './PageHeaderActionsMenu';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.system.status,
|
||||
(status) => {
|
||||
return {
|
||||
formsAuth: status.item.authentication === 'forms'
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
restart,
|
||||
shutdown
|
||||
};
|
||||
|
||||
class PageHeaderActionsMenuConnector extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onRestartPress = () => {
|
||||
this.props.restart();
|
||||
};
|
||||
|
||||
onShutdownPress = () => {
|
||||
this.props.shutdown();
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<PageHeaderActionsMenu
|
||||
{...this.props}
|
||||
onRestartPress={this.onRestartPress}
|
||||
onShutdownPress={this.onShutdownPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PageHeaderActionsMenuConnector.propTypes = {
|
||||
restart: PropTypes.func.isRequired,
|
||||
shutdown: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(PageHeaderActionsMenuConnector);
|
||||
@@ -54,18 +54,20 @@ function getAverageResponseTimeData(indexerStats: IndexerStatsIndexer[]) {
|
||||
}
|
||||
|
||||
function getFailureRateData(indexerStats: IndexerStatsIndexer[]) {
|
||||
const data = indexerStats.map((indexer) => ({
|
||||
label: indexer.indexerName,
|
||||
value:
|
||||
(indexer.numberOfFailedQueries +
|
||||
indexer.numberOfFailedRssQueries +
|
||||
indexer.numberOfFailedAuthQueries +
|
||||
indexer.numberOfFailedGrabs) /
|
||||
(indexer.numberOfQueries +
|
||||
indexer.numberOfRssQueries +
|
||||
indexer.numberOfAuthQueries +
|
||||
indexer.numberOfGrabs),
|
||||
}));
|
||||
const data = [...indexerStats]
|
||||
.map((indexer) => ({
|
||||
label: indexer.indexerName,
|
||||
value:
|
||||
(indexer.numberOfFailedQueries +
|
||||
indexer.numberOfFailedRssQueries +
|
||||
indexer.numberOfFailedAuthQueries +
|
||||
indexer.numberOfFailedGrabs) /
|
||||
(indexer.numberOfQueries +
|
||||
indexer.numberOfRssQueries +
|
||||
indexer.numberOfAuthQueries +
|
||||
indexer.numberOfGrabs),
|
||||
}))
|
||||
.filter((s) => s.value > 0);
|
||||
|
||||
data.sort((a, b) => b.value - a.value);
|
||||
|
||||
@@ -73,13 +75,20 @@ function getFailureRateData(indexerStats: IndexerStatsIndexer[]) {
|
||||
}
|
||||
|
||||
function getTotalRequestsData(indexerStats: IndexerStatsIndexer[]) {
|
||||
const statistics = [...indexerStats].sort(
|
||||
(a, b) =>
|
||||
b.numberOfQueries +
|
||||
b.numberOfRssQueries +
|
||||
b.numberOfAuthQueries -
|
||||
(a.numberOfQueries + a.numberOfRssQueries + a.numberOfAuthQueries)
|
||||
);
|
||||
const statistics = [...indexerStats]
|
||||
.filter(
|
||||
(s) =>
|
||||
s.numberOfQueries > 0 ||
|
||||
s.numberOfRssQueries > 0 ||
|
||||
s.numberOfAuthQueries > 0
|
||||
)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
b.numberOfQueries +
|
||||
b.numberOfRssQueries +
|
||||
b.numberOfAuthQueries -
|
||||
(a.numberOfQueries + a.numberOfRssQueries + a.numberOfAuthQueries)
|
||||
);
|
||||
|
||||
return {
|
||||
labels: statistics.map((indexer) => indexer.indexerName),
|
||||
@@ -101,10 +110,12 @@ function getTotalRequestsData(indexerStats: IndexerStatsIndexer[]) {
|
||||
}
|
||||
|
||||
function getNumberGrabsData(indexerStats: IndexerStatsIndexer[]) {
|
||||
const data = indexerStats.map((indexer) => ({
|
||||
label: indexer.indexerName,
|
||||
value: indexer.numberOfGrabs - indexer.numberOfFailedGrabs,
|
||||
}));
|
||||
const data = [...indexerStats]
|
||||
.map((indexer) => ({
|
||||
label: indexer.indexerName,
|
||||
value: indexer.numberOfGrabs - indexer.numberOfFailedGrabs,
|
||||
}))
|
||||
.filter((s) => s.value > 0);
|
||||
|
||||
data.sort((a, b) => b.value - a.value);
|
||||
|
||||
|
||||
@@ -110,7 +110,6 @@ export const defaultState = {
|
||||
{
|
||||
name: 'actions',
|
||||
columnLabel: () => translate('Actions'),
|
||||
isSortable: true,
|
||||
isVisible: true,
|
||||
isModifiable: false
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
"ts-loader": "9.4.2",
|
||||
"typescript-plugin-css-modules": "5.0.1",
|
||||
"url-loader": "4.1.1",
|
||||
"webpack": "5.89.0",
|
||||
"webpack": "5.94.0",
|
||||
"webpack-cli": "5.1.4",
|
||||
"webpack-livereload-plugin": "3.0.2"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
|
||||
namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
@@ -18,5 +19,24 @@ namespace NzbDrone.Common.Extensions
|
||||
|
||||
return Uri.TryCreate(path, UriKind.Absolute, out var uri) && uri.IsWellFormedOriginalString();
|
||||
}
|
||||
|
||||
public static Uri RemoveQueryParam(this Uri url, string name)
|
||||
{
|
||||
var uriBuilder = new UriBuilder(url);
|
||||
var query = HttpUtility.ParseQueryString(uriBuilder.Query);
|
||||
|
||||
query.Remove(name);
|
||||
uriBuilder.Query = query.ToString() ?? string.Empty;
|
||||
|
||||
return uriBuilder.Uri;
|
||||
}
|
||||
|
||||
public static string GetQueryParam(this Uri url, string name)
|
||||
{
|
||||
var uriBuilder = new UriBuilder(url);
|
||||
var query = HttpUtility.ParseQueryString(uriBuilder.Query);
|
||||
|
||||
return query[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@ namespace NzbDrone.Common.Http.Proxy
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(BypassFilter))
|
||||
{
|
||||
var hostlist = BypassFilter.Split(',');
|
||||
var hostlist = BypassFilter.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
|
||||
for (var i = 0; i < hostlist.Length; i++)
|
||||
{
|
||||
if (hostlist[i].StartsWith("*"))
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DryIoc.dll" Version="5.4.3" />
|
||||
<PackageReference Include="IPAddressRange" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace NzbDrone.Core.Test.Http
|
||||
{
|
||||
private HttpProxySettings GetProxySettings()
|
||||
{
|
||||
return new HttpProxySettings(ProxyType.Socks5, "localhost", 8080, "*.httpbin.org,google.com", true, null, null);
|
||||
return new HttpProxySettings(ProxyType.Socks5, "localhost", 8080, "*.httpbin.org,google.com,172.16.0.0/12", true, null, null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -23,6 +23,7 @@ namespace NzbDrone.Core.Test.Http
|
||||
Subject.ShouldProxyBeBypassed(settings, new HttpUri("http://eu.httpbin.org/get")).Should().BeTrue();
|
||||
Subject.ShouldProxyBeBypassed(settings, new HttpUri("http://google.com/get")).Should().BeTrue();
|
||||
Subject.ShouldProxyBeBypassed(settings, new HttpUri("http://localhost:8654/get")).Should().BeTrue();
|
||||
Subject.ShouldProxyBeBypassed(settings, new HttpUri("http://172.21.0.1:8989/api/v3/indexer/schema")).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -31,6 +32,7 @@ namespace NzbDrone.Core.Test.Http
|
||||
var settings = GetProxySettings();
|
||||
|
||||
Subject.ShouldProxyBeBypassed(settings, new HttpUri("http://bing.com/get")).Should().BeFalse();
|
||||
Subject.ShouldProxyBeBypassed(settings, new HttpUri("http://172.3.0.1:8989/api/v3/indexer/schema")).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,6 +165,13 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
Priority = indexer.Priority
|
||||
};
|
||||
|
||||
if (indexer.Protocol == DownloadProtocol.Torrent)
|
||||
{
|
||||
lazyLibrarianIndexer.MinimumSeeders = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.AppMinimumSeeders ?? indexer.AppProfile.Value.MinimumSeeders;
|
||||
lazyLibrarianIndexer.SeedRatio = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.SeedRatio.GetValueOrDefault();
|
||||
lazyLibrarianIndexer.SeedTime = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.SeedTime.GetValueOrDefault();
|
||||
}
|
||||
|
||||
return lazyLibrarianIndexer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,9 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
public string Altername { get; set; }
|
||||
public LazyLibrarianProviderType Type { get; set; }
|
||||
public int Priority { get; set; }
|
||||
public double SeedRatio { get; set; }
|
||||
public int SeedTime { get; set; }
|
||||
public int MinimumSeeders { get; set; }
|
||||
|
||||
public bool Equals(LazyLibrarianIndexer other)
|
||||
{
|
||||
@@ -45,7 +48,10 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
other.Categories == Categories &&
|
||||
other.Enabled == Enabled &&
|
||||
other.Altername == Altername &&
|
||||
other.Priority == Priority;
|
||||
other.Priority == Priority &&
|
||||
other.SeedRatio == SeedRatio &&
|
||||
other.SeedTime == SeedTime &&
|
||||
other.MinimumSeeders == MinimumSeeders;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using FluentValidation.Results;
|
||||
@@ -96,6 +97,13 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
{ "dlpriority", CalculatePriority(indexer.Priority).ToString() }
|
||||
};
|
||||
|
||||
if (indexer.Type == LazyLibrarianProviderType.Torznab)
|
||||
{
|
||||
parameters.Add("seeders", indexer.MinimumSeeders.ToString());
|
||||
parameters.Add("seed_ratio", indexer.SeedRatio.ToString(CultureInfo.InvariantCulture));
|
||||
parameters.Add("seed_duration", indexer.SeedTime.ToString());
|
||||
}
|
||||
|
||||
var request = BuildRequest(settings, "/api", "addProvider", HttpMethod.Get, parameters);
|
||||
CheckForError(Execute<LazyLibrarianStatus>(request));
|
||||
return indexer;
|
||||
@@ -115,6 +123,13 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
{ "dlpriority", CalculatePriority(indexer.Priority).ToString() }
|
||||
};
|
||||
|
||||
if (indexer.Type == LazyLibrarianProviderType.Torznab)
|
||||
{
|
||||
parameters.Add("seeders", indexer.MinimumSeeders.ToString());
|
||||
parameters.Add("seed_ratio", indexer.SeedRatio.ToString(CultureInfo.InvariantCulture));
|
||||
parameters.Add("seed_duration", indexer.SeedTime.ToString());
|
||||
}
|
||||
|
||||
var request = BuildRequest(settings, "/api", "changeProvider", HttpMethod.Get, parameters);
|
||||
CheckForError(Execute<LazyLibrarianStatus>(request));
|
||||
return indexer;
|
||||
|
||||
@@ -384,7 +384,7 @@ namespace NzbDrone.Core.Configuration
|
||||
}
|
||||
|
||||
// If SSL is enabled and a cert hash is still in the config file or cert path is empty disable SSL
|
||||
if (EnableSsl && (GetValue("SslCertHash", null).IsNotNullOrWhiteSpace() || SslCertPath.IsNullOrWhiteSpace()))
|
||||
if (EnableSsl && (GetValue("SslCertHash", string.Empty, false).IsNotNullOrWhiteSpace() || SslCertPath.IsNullOrWhiteSpace()))
|
||||
{
|
||||
SetValue("EnableSsl", false);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using Dapper;
|
||||
using FluentMigrator;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(041)]
|
||||
public class gazelle_freeleech_token_options : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Execute.WithConnection(MigrateIndexersToTokenOptions);
|
||||
}
|
||||
|
||||
private void MigrateIndexersToTokenOptions(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
var updated = new List<object>();
|
||||
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.Transaction = tran;
|
||||
cmd.CommandText = "SELECT \"Id\", \"Settings\" FROM \"Indexers\" WHERE \"Implementation\" IN ('Orpheus', 'Redacted', 'AlphaRatio', 'BrokenStones', 'CGPeers', 'DICMusic', 'GreatPosterWall', 'SecretCinema')";
|
||||
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
var id = reader.GetInt32(0);
|
||||
var settings = Json.Deserialize<JObject>(reader.GetString(1));
|
||||
|
||||
if (settings.ContainsKey("useFreeleechToken") && settings.Value<JToken>("useFreeleechToken").Type == JTokenType.Boolean)
|
||||
{
|
||||
var optionValue = settings.Value<bool>("useFreeleechToken") switch
|
||||
{
|
||||
true => 2, // Required
|
||||
_ => 0 // Never
|
||||
};
|
||||
|
||||
settings.Remove("useFreeleechToken");
|
||||
settings.Add("useFreeleechToken", optionValue);
|
||||
}
|
||||
|
||||
updated.Add(new
|
||||
{
|
||||
Id = id,
|
||||
Settings = settings.ToJson()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var updateSql = "UPDATE \"Indexers\" SET \"Settings\" = @Settings WHERE \"Id\" = @Id";
|
||||
conn.Execute(updateSql, updated, transaction: tran);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using NetTools;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Http.Proxy;
|
||||
using NzbDrone.Core.Configuration;
|
||||
@@ -52,7 +54,15 @@ namespace NzbDrone.Core.Http
|
||||
//We are utilizing the WebProxy implementation here to save us having to re-implement it. This way we use Microsofts implementation
|
||||
var proxy = new WebProxy(proxySettings.Host + ":" + proxySettings.Port, proxySettings.BypassLocalAddress, proxySettings.BypassListAsArray);
|
||||
|
||||
return proxy.IsBypassed((Uri)url);
|
||||
return proxy.IsBypassed((Uri)url) || IsBypassedByIpAddressRange(proxySettings.BypassListAsArray, url.Host);
|
||||
}
|
||||
|
||||
private static bool IsBypassedByIpAddressRange(string[] bypassList, string host)
|
||||
{
|
||||
return bypassList.Any(bypass =>
|
||||
IPAddressRange.TryParse(bypass, out var ipAddressRange) &&
|
||||
IPAddress.TryParse(host, out var ipAddress) &&
|
||||
ipAddressRange.Contains(ipAddress));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ public class AlphaRatioParser : GazelleParser
|
||||
.AddQueryParam("action", "download")
|
||||
.AddQueryParam("id", torrentId);
|
||||
|
||||
if (Settings.UseFreeleechToken && canUseToken)
|
||||
if (Settings.UseFreeleechToken is (int)GazelleFreeleechTokenAction.Preferred or (int)GazelleFreeleechTokenAction.Required && canUseToken)
|
||||
{
|
||||
url = url.AddQueryParam("usetoken", "1");
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
if (searchCriteria.IsRssSearch)
|
||||
{
|
||||
cleanReleases = cleanReleases.Where((r, index) => r.PublishDate > DateTime.Now.AddDays(-1) || index < 20).ToList();
|
||||
cleanReleases = cleanReleases.Where((r, index) => r.PublishDate > DateTime.UtcNow.AddDays(-1) || index < 20).ToList();
|
||||
}
|
||||
|
||||
return cleanReleases.Select(r => (ReleaseInfo)r.Clone()).ToList();
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
@@ -85,22 +86,23 @@ public abstract class GazelleBase<TSettings> : TorrentIndexerBase<TSettings>
|
||||
public override async Task<IndexerDownloadResponse> Download(Uri link)
|
||||
{
|
||||
var downloadResponse = await base.Download(link);
|
||||
var response = downloadResponse.Data;
|
||||
|
||||
if (response.Length >= 1
|
||||
&& response[0] != 'd' // simple test for torrent vs HTML content
|
||||
var fileData = downloadResponse.Data;
|
||||
|
||||
if (Settings.UseFreeleechToken == (int)GazelleFreeleechTokenAction.Preferred
|
||||
&& fileData.Length >= 1
|
||||
&& fileData[0] != 'd' // simple test for torrent vs HTML content
|
||||
&& link.Query.Contains("usetoken=1"))
|
||||
{
|
||||
var html = Encoding.GetString(response);
|
||||
var html = Encoding.GetString(fileData);
|
||||
|
||||
if (html.Contains("You do not have any freeleech tokens left.")
|
||||
|| html.Contains("You do not have enough freeleech tokens")
|
||||
|| html.Contains("This torrent is too large.")
|
||||
|| html.Contains("You cannot use tokens here"))
|
||||
{
|
||||
// download again without usetoken=1
|
||||
var requestLinkNew = link.ToString().Replace("&usetoken=1", "");
|
||||
|
||||
downloadResponse = await base.Download(new Uri(requestLinkNew));
|
||||
// Try to download again without usetoken
|
||||
downloadResponse = await base.Download(link.RemoveQueryParam("usetoken"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,8 +59,10 @@ public class GazelleParser : IParseIndexerResponse
|
||||
{
|
||||
foreach (var torrent in result.Torrents)
|
||||
{
|
||||
var isFreeLeech = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsPersonalFreeLeech;
|
||||
|
||||
// skip releases that cannot be used with freeleech tokens when the option is enabled
|
||||
if (Settings.UseFreeleechToken && !torrent.CanUseToken)
|
||||
if (Settings.UseFreeleechToken == (int)GazelleFreeleechTokenAction.Required && !torrent.CanUseToken && !isFreeLeech)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -79,7 +81,7 @@ public class GazelleParser : IParseIndexerResponse
|
||||
{
|
||||
Guid = infoUrl,
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = GetDownloadUrl(id, !torrent.IsFreeLeech && !torrent.IsNeutralLeech && !torrent.IsPersonalFreeLeech),
|
||||
DownloadUrl = GetDownloadUrl(id, torrent.CanUseToken && !isFreeLeech),
|
||||
Title = WebUtility.HtmlDecode(title),
|
||||
Container = torrent.Encoding,
|
||||
Files = torrent.FileCount,
|
||||
@@ -91,7 +93,7 @@ public class GazelleParser : IParseIndexerResponse
|
||||
PublishDate = torrent.Time.ToUniversalTime(),
|
||||
Scene = torrent.Scene,
|
||||
PosterUrl = posterUrl,
|
||||
DownloadVolumeFactor = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsPersonalFreeLeech ? 0 : 1,
|
||||
DownloadVolumeFactor = isFreeLeech ? 0 : 1,
|
||||
UploadVolumeFactor = torrent.IsNeutralLeech ? 0 : 1
|
||||
};
|
||||
|
||||
@@ -110,8 +112,10 @@ public class GazelleParser : IParseIndexerResponse
|
||||
}
|
||||
else
|
||||
{
|
||||
var isFreeLeech = result.IsFreeLeech || result.IsNeutralLeech || result.IsPersonalFreeLeech;
|
||||
|
||||
// skip releases that cannot be used with freeleech tokens when the option is enabled
|
||||
if (Settings.UseFreeleechToken && !result.CanUseToken)
|
||||
if (Settings.UseFreeleechToken == (int)GazelleFreeleechTokenAction.Required && !result.CanUseToken && !isFreeLeech)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -124,7 +128,7 @@ public class GazelleParser : IParseIndexerResponse
|
||||
{
|
||||
Guid = infoUrl,
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = GetDownloadUrl(id, !result.IsFreeLeech && !result.IsNeutralLeech && !result.IsPersonalFreeLeech),
|
||||
DownloadUrl = GetDownloadUrl(id, result.CanUseToken && !isFreeLeech),
|
||||
Title = groupName,
|
||||
Size = long.Parse(result.Size),
|
||||
Seeders = int.Parse(result.Seeders),
|
||||
@@ -133,7 +137,7 @@ public class GazelleParser : IParseIndexerResponse
|
||||
Grabs = result.Snatches,
|
||||
PublishDate = long.TryParse(result.GroupTime, out var num) ? DateTimeOffset.FromUnixTimeSeconds(num).UtcDateTime : DateTimeUtil.FromFuzzyTime((string)result.GroupTime),
|
||||
PosterUrl = posterUrl,
|
||||
DownloadVolumeFactor = result.IsFreeLeech || result.IsNeutralLeech || result.IsPersonalFreeLeech ? 0 : 1,
|
||||
DownloadVolumeFactor = isFreeLeech ? 0 : 1,
|
||||
UploadVolumeFactor = result.IsNeutralLeech ? 0 : 1
|
||||
};
|
||||
|
||||
@@ -165,7 +169,7 @@ public class GazelleParser : IParseIndexerResponse
|
||||
.AddQueryParam("action", "download")
|
||||
.AddQueryParam("id", torrentId);
|
||||
|
||||
if (Settings.UseFreeleechToken)
|
||||
if (Settings.UseFreeleechToken is (int)GazelleFreeleechTokenAction.Preferred or (int)GazelleFreeleechTokenAction.Required && canUseToken)
|
||||
{
|
||||
url = url.AddQueryParam("usetoken", "1");
|
||||
}
|
||||
|
||||
@@ -16,11 +16,23 @@ public class GazelleSettings : UserPassTorrentBaseSettings
|
||||
public string AuthKey { get; set; }
|
||||
public string PassKey { get; set; }
|
||||
|
||||
[FieldDefinition(5, Type = FieldType.Checkbox, Label = "Use Freeleech Token", HelpText = "Use freeleech tokens when available")]
|
||||
public bool UseFreeleechToken { get; set; }
|
||||
[FieldDefinition(5, Type = FieldType.Select, Label = "Use Freeleech Tokens", SelectOptions = typeof(GazelleFreeleechTokenAction), HelpText = "When to use freeleech tokens")]
|
||||
public int UseFreeleechToken { get; set; }
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
|
||||
public enum GazelleFreeleechTokenAction
|
||||
{
|
||||
[FieldOption(Label = "Never", Hint = "Do not use tokens")]
|
||||
Never = 0,
|
||||
|
||||
[FieldOption(Label = "Preferred", Hint = "Use token if possible")]
|
||||
Preferred = 1,
|
||||
|
||||
[FieldOption(Label = "Required", Hint = "Abort download if unable to use token")]
|
||||
Required = 2,
|
||||
}
|
||||
|
||||
@@ -168,8 +168,10 @@ public class GreatPosterWallParser : GazelleParser
|
||||
{
|
||||
foreach (var torrent in result.Torrents)
|
||||
{
|
||||
var isFreeLeech = torrent.IsFreeleech || torrent.IsNeutralLeech || torrent.IsPersonalFreeleech;
|
||||
|
||||
// skip releases that cannot be used with freeleech tokens when the option is enabled
|
||||
if (_settings.UseFreeleechToken && !torrent.CanUseToken)
|
||||
if (_settings.UseFreeleechToken == (int)GazelleFreeleechTokenAction.Required && !torrent.CanUseToken && !isFreeLeech)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -181,7 +183,7 @@ public class GreatPosterWallParser : GazelleParser
|
||||
{
|
||||
Guid = infoUrl,
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = GetDownloadUrl(torrent.TorrentId, !torrent.IsFreeleech && !torrent.IsNeutralLeech && !torrent.IsPersonalFreeleech),
|
||||
DownloadUrl = GetDownloadUrl(torrent.TorrentId, torrent.CanUseToken && !isFreeLeech),
|
||||
Title = WebUtility.HtmlDecode(torrent.FileName).Trim(),
|
||||
PosterUrl = GetPosterUrl(result.Cover),
|
||||
PublishDate = new DateTimeOffset(time, TimeSpan.FromHours(8)).UtcDateTime, // Time is Chinese Time, add 8 hours difference from UTC
|
||||
@@ -192,7 +194,7 @@ public class GreatPosterWallParser : GazelleParser
|
||||
Grabs = torrent.Snatches,
|
||||
Files = torrent.FileCount,
|
||||
Scene = torrent.Scene,
|
||||
DownloadVolumeFactor = torrent.IsFreeleech || torrent.IsNeutralLeech || torrent.IsPersonalFreeleech ? 0 : 1,
|
||||
DownloadVolumeFactor = isFreeLeech ? 0 : 1,
|
||||
UploadVolumeFactor = torrent.IsNeutralLeech ? 0 : 1,
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 172800 // 48 hours
|
||||
@@ -240,7 +242,7 @@ public class GreatPosterWallParser : GazelleParser
|
||||
.AddQueryParam("action", "download")
|
||||
.AddQueryParam("id", torrentId);
|
||||
|
||||
if (_settings.UseFreeleechToken && canUseToken)
|
||||
if (_settings.UseFreeleechToken is (int)GazelleFreeleechTokenAction.Preferred or (int)GazelleFreeleechTokenAction.Required && canUseToken)
|
||||
{
|
||||
url = url.AddQueryParam("usetoken", "1");
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
@@ -55,11 +56,13 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public override async Task<IndexerDownloadResponse> Download(Uri link)
|
||||
{
|
||||
if (Settings.Freeleech)
|
||||
{
|
||||
_logger.Debug($"Attempting to use freeleech token for {link.AbsoluteUri}");
|
||||
var downloadLink = link.RemoveQueryParam("canUseToken");
|
||||
|
||||
var idMatch = TorrentIdRegex.Match(link.AbsoluteUri);
|
||||
if (Settings.Freeleech && bool.TryParse(link.GetQueryParam("canUseToken"), out var canUseToken) && canUseToken)
|
||||
{
|
||||
_logger.Debug("Attempting to use freeleech token for {0}", downloadLink.AbsoluteUri);
|
||||
|
||||
var idMatch = TorrentIdRegex.Match(downloadLink.AbsoluteUri);
|
||||
if (idMatch.Success)
|
||||
{
|
||||
var id = int.Parse(idMatch.Groups["id"].Value);
|
||||
@@ -78,25 +81,41 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
if (resource.Success)
|
||||
{
|
||||
_logger.Debug($"Successfully to used freeleech token for torrentid ${id}");
|
||||
_logger.Debug("Successfully to used freeleech token for torrentid {0}", id);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Debug($"Failed to use freeleech token: ${resource.Error}");
|
||||
_logger.Debug("Failed to use freeleech token: {0}", resource.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Debug($"Could not get torrent id from link ${link.AbsoluteUri}, skipping freeleech");
|
||||
_logger.Debug("Could not get torrent id from link {0}, skipping freeleech", downloadLink.AbsoluteUri);
|
||||
}
|
||||
}
|
||||
|
||||
return await base.Download(link).ConfigureAwait(false);
|
||||
return await base.Download(downloadLink).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
protected override IDictionary<string, string> GetCookies()
|
||||
{
|
||||
return CookieUtil.CookieHeaderToDictionary("mam_id=" + Settings.MamId);
|
||||
var cookies = base.GetCookies();
|
||||
|
||||
if (cookies is { Count: > 0 } && cookies.TryGetValue("mam_id", out var mamId) && mamId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
return cookies;
|
||||
}
|
||||
|
||||
return CookieUtil.CookieHeaderToDictionary($"mam_id={Settings.MamId}");
|
||||
}
|
||||
|
||||
protected override async Task Test(List<ValidationFailure> failures)
|
||||
{
|
||||
UpdateCookies(null, null);
|
||||
|
||||
_logger.Debug("Cookies cleared.");
|
||||
|
||||
await base.Test(failures).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
@@ -359,11 +378,12 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
private readonly IIndexerHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private readonly ICached<string> _userClassCache;
|
||||
private readonly HashSet<string> _vipFreeleechUserClasses = new (StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"VIP",
|
||||
"Elite VIP",
|
||||
"Elite VIP"
|
||||
};
|
||||
|
||||
public MyAnonamouseParser(MyAnonamouseSettings settings,
|
||||
@@ -376,32 +396,35 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
_categories = categories;
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
|
||||
_userClassCache = cacheManager.GetCache<string>(GetType());
|
||||
}
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var httpResponse = indexerResponse.HttpResponse;
|
||||
|
||||
// Throw auth errors here before we try to parse
|
||||
if (indexerResponse.HttpResponse.StatusCode == HttpStatusCode.Forbidden)
|
||||
if (httpResponse.StatusCode == HttpStatusCode.Forbidden)
|
||||
{
|
||||
throw new IndexerAuthException("[403 Forbidden] - mam_id expired or invalid");
|
||||
}
|
||||
|
||||
// Throw common http errors here before we try to parse
|
||||
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
|
||||
if (httpResponse.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
// Remove cookie cache
|
||||
CookiesUpdater(null, null);
|
||||
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request");
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response status {httpResponse.StatusCode} code from indexer request");
|
||||
}
|
||||
|
||||
if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value))
|
||||
if (!httpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value))
|
||||
{
|
||||
// Remove cookie cache
|
||||
CookiesUpdater(null, null);
|
||||
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from indexer request, expected {HttpAccept.Json.Value}");
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response header {httpResponse.Headers.ContentType} from indexer request, expected {HttpAccept.Json.Value}");
|
||||
}
|
||||
|
||||
var releaseInfos = new List<ReleaseInfo>();
|
||||
@@ -414,7 +437,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
return releaseInfos.ToArray();
|
||||
}
|
||||
|
||||
var hasUserVip = HasUserVip();
|
||||
var hasUserVip = HasUserVip(httpResponse.GetCookies());
|
||||
|
||||
foreach (var item in jsonResponse.Data)
|
||||
{
|
||||
@@ -464,8 +487,10 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
release.Title += " [VIP]";
|
||||
}
|
||||
|
||||
release.DownloadUrl = _settings.BaseUrl + "tor/download.php?tid=" + id;
|
||||
release.InfoUrl = _settings.BaseUrl + "t/" + id;
|
||||
var isFreeLeech = item.Free || item.PersonalFreeLeech || (hasUserVip && item.FreeVip);
|
||||
|
||||
release.DownloadUrl = GetDownloadUrl(id, !isFreeLeech);
|
||||
release.InfoUrl = $"{_settings.BaseUrl}t/{id}";
|
||||
release.Guid = release.InfoUrl;
|
||||
release.Categories = _categories.MapTrackerCatToNewznab(item.Category);
|
||||
release.PublishDate = DateTime.ParseExact(item.Added, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime();
|
||||
@@ -474,7 +499,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
release.Seeders = item.Seeders;
|
||||
release.Peers = item.Leechers + release.Seeders;
|
||||
release.Size = ParseUtil.GetBytes(item.Size);
|
||||
release.DownloadVolumeFactor = item.Free ? 0 : hasUserVip && item.FreeVip ? 0 : 1;
|
||||
release.DownloadVolumeFactor = isFreeLeech ? 0 : 1;
|
||||
release.UploadVolumeFactor = 1;
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 259200; // 72 hours
|
||||
@@ -482,10 +507,27 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
releaseInfos.Add(release);
|
||||
}
|
||||
|
||||
// Update cookies with the updated mam_id value received in the response
|
||||
CookiesUpdater(httpResponse.GetCookies(), DateTime.Now.AddDays(30));
|
||||
|
||||
return releaseInfos.ToArray();
|
||||
}
|
||||
|
||||
private bool HasUserVip()
|
||||
private string GetDownloadUrl(int torrentId, bool canUseToken)
|
||||
{
|
||||
var url = new HttpUri(_settings.BaseUrl)
|
||||
.CombinePath("/tor/download.php")
|
||||
.AddQueryParam("tid", torrentId);
|
||||
|
||||
if (_settings.Freeleech && canUseToken)
|
||||
{
|
||||
url = url.AddQueryParam("canUseToken", "true");
|
||||
}
|
||||
|
||||
return url.FullUri;
|
||||
}
|
||||
|
||||
private bool HasUserVip(Dictionary<string, string> cookies)
|
||||
{
|
||||
var cacheKey = "myanonamouse_user_class_" + _settings.ToJson().SHA256Hash();
|
||||
|
||||
@@ -496,11 +538,10 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var request = new HttpRequestBuilder(_settings.BaseUrl.Trim('/'))
|
||||
.Resource("/jsonLoad.php")
|
||||
.Accept(HttpAccept.Json)
|
||||
.SetCookies(cookies)
|
||||
.Build();
|
||||
|
||||
_logger.Debug("Fetching user data: " + request.Url.FullUri);
|
||||
|
||||
request.Cookies.Add("mam_id", _settings.MamId);
|
||||
_logger.Debug("Fetching user data: {0}", request.Url.FullUri);
|
||||
|
||||
var response = _httpClient.Get(request);
|
||||
var jsonResponse = JsonConvert.DeserializeObject<MyAnonamouseUserDataResponse>(response.Content);
|
||||
@@ -564,14 +605,19 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
[FieldOption(Label="All torrents", Hint = "Search everything")]
|
||||
All = 0,
|
||||
|
||||
[FieldOption(Label="Only active", Hint = "Last update had 1+ seeders")]
|
||||
Active = 1,
|
||||
|
||||
[FieldOption(Label="Freeleech", Hint = "Freeleech torrents")]
|
||||
Freeleech = 2,
|
||||
|
||||
[FieldOption(Label="Freeleech or VIP", Hint = "Freeleech or VIP torrents")]
|
||||
FreeleechOrVip = 3,
|
||||
|
||||
[FieldOption(Label="VIP", Hint = "VIP torrents")]
|
||||
Vip = 4,
|
||||
|
||||
[FieldOption(Label="Not VIP", Hint = "Torrents not VIP")]
|
||||
NotVip = 5,
|
||||
}
|
||||
@@ -588,6 +634,8 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public string Filetype { get; set; }
|
||||
public bool Vip { get; set; }
|
||||
public bool Free { get; set; }
|
||||
[JsonProperty(PropertyName = "personal_freeleech")]
|
||||
public bool PersonalFreeLeech { get; set; }
|
||||
[JsonProperty(PropertyName = "fl_vip")]
|
||||
public bool FreeVip { get; set; }
|
||||
public string Category { get; set; }
|
||||
|
||||
@@ -244,7 +244,9 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new IndexerException(indexerResponse, "Unexpected response status '{0}' code from indexer request", indexerResponse.HttpResponse.StatusCode);
|
||||
STJson.TryDeserialize<JsonRpcResponse<NebulanceErrorResponse>>(indexerResponse.HttpResponse.Content, out var errorResponse);
|
||||
|
||||
throw new IndexerException(indexerResponse, "Unexpected response status '{0}' code from indexer request: {1}", indexerResponse.HttpResponse.StatusCode, errorResponse?.Result?.Error?.Message ?? "Check the logs for more information.");
|
||||
}
|
||||
|
||||
JsonRpcResponse<NebulanceResponse> jsonResponse;
|
||||
@@ -410,4 +412,14 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public IEnumerable<string> Tags { get; set; } = Array.Empty<string>();
|
||||
}
|
||||
|
||||
public class NebulanceErrorResponse
|
||||
{
|
||||
public NebulanceErrorMessage Error { get; set; }
|
||||
}
|
||||
|
||||
public class NebulanceErrorMessage
|
||||
{
|
||||
public string Message { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,20 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
return new OrpheusParser(Settings, Capabilities.Categories);
|
||||
}
|
||||
|
||||
protected override Task<HttpRequest> GetDownloadRequest(Uri link)
|
||||
{
|
||||
var requestBuilder = new HttpRequestBuilder(link.AbsoluteUri)
|
||||
{
|
||||
AllowAutoRedirect = FollowRedirect
|
||||
};
|
||||
|
||||
var request = requestBuilder
|
||||
.SetHeader("Authorization", $"token {Settings.Apikey}")
|
||||
.Build();
|
||||
|
||||
return Task.FromResult(request);
|
||||
}
|
||||
|
||||
protected override IList<ReleaseInfo> CleanupReleases(IEnumerable<ReleaseInfo> releases, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
var cleanReleases = base.CleanupReleases(releases, searchCriteria);
|
||||
@@ -91,47 +105,28 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public override async Task<IndexerDownloadResponse> Download(Uri link)
|
||||
{
|
||||
var request = new HttpRequestBuilder(link.AbsoluteUri)
|
||||
.SetHeader("Authorization", $"token {Settings.Apikey}")
|
||||
.Build();
|
||||
var downloadResponse = await base.Download(link);
|
||||
|
||||
byte[] downloadBytes;
|
||||
long elapsedTime;
|
||||
var fileData = downloadResponse.Data;
|
||||
|
||||
try
|
||||
if (Settings.UseFreeleechToken == (int)OrpheusFreeleechTokenAction.Preferred
|
||||
&& fileData.Length >= 1
|
||||
&& fileData[0] != 'd' // simple test for torrent vs HTML content
|
||||
&& link.Query.Contains("usetoken=1"))
|
||||
{
|
||||
var response = await _httpClient.ExecuteProxiedAsync(request, Definition);
|
||||
downloadBytes = response.ResponseData;
|
||||
elapsedTime = response.ElapsedTime;
|
||||
var html = Encoding.GetString(fileData);
|
||||
|
||||
if (downloadBytes.Length >= 1
|
||||
&& downloadBytes[0] != 'd' // simple test for torrent vs HTML content
|
||||
&& link.Query.Contains("usetoken=1"))
|
||||
if (html.Contains("You do not have any freeleech tokens left.")
|
||||
|| html.Contains("You do not have enough freeleech tokens")
|
||||
|| html.Contains("This torrent is too large.")
|
||||
|| html.Contains("You cannot use tokens here"))
|
||||
{
|
||||
var html = Encoding.GetString(downloadBytes);
|
||||
if (html.Contains("You do not have any freeleech tokens left.")
|
||||
|| html.Contains("You do not have enough freeleech tokens")
|
||||
|| html.Contains("This torrent is too large.")
|
||||
|| html.Contains("You cannot use tokens here"))
|
||||
{
|
||||
// download again without usetoken
|
||||
request.Url = new HttpUri(link.ToString().Replace("&usetoken=1", ""));
|
||||
|
||||
response = await _httpClient.ExecuteProxiedAsync(request, Definition);
|
||||
downloadBytes = response.ResponseData;
|
||||
}
|
||||
// Try to download again without usetoken
|
||||
downloadResponse = await base.Download(link.RemoveQueryParam("usetoken"));
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_indexerStatusService.RecordFailure(Definition.Id);
|
||||
_logger.Error("Download failed");
|
||||
throw;
|
||||
}
|
||||
|
||||
ValidateDownloadData(downloadBytes);
|
||||
|
||||
return new IndexerDownloadResponse(downloadBytes, elapsedTime);
|
||||
return downloadResponse;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,7 +275,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
foreach (var torrent in result.Torrents)
|
||||
{
|
||||
// skip releases that cannot be used with freeleech tokens when the option is enabled
|
||||
if (_settings.UseFreeleechToken && !torrent.CanUseToken)
|
||||
if (_settings.UseFreeleechToken == (int)OrpheusFreeleechTokenAction.Required && !torrent.CanUseToken)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -289,12 +284,13 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
var title = GetTitle(result, torrent);
|
||||
var infoUrl = GetInfoUrl(result.GroupId, id);
|
||||
var isFreeLeech = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsPersonalFreeLeech;
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Guid = infoUrl,
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = GetDownloadUrl(id, !torrent.IsFreeLeech && !torrent.IsNeutralLeech && !torrent.IsPersonalFreeLeech),
|
||||
DownloadUrl = GetDownloadUrl(id, torrent.CanUseToken && !isFreeLeech),
|
||||
Title = WebUtility.HtmlDecode(title),
|
||||
Artist = WebUtility.HtmlDecode(result.Artist),
|
||||
Album = WebUtility.HtmlDecode(result.GroupName),
|
||||
@@ -308,7 +304,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
Scene = torrent.Scene,
|
||||
Files = torrent.FileCount,
|
||||
Grabs = torrent.Snatches,
|
||||
DownloadVolumeFactor = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsPersonalFreeLeech ? 0 : 1,
|
||||
DownloadVolumeFactor = isFreeLeech ? 0 : 1,
|
||||
UploadVolumeFactor = torrent.IsNeutralLeech ? 0 : 1
|
||||
};
|
||||
|
||||
@@ -330,19 +326,20 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
else
|
||||
{
|
||||
// skip releases that cannot be used with freeleech tokens when the option is enabled
|
||||
if (_settings.UseFreeleechToken && !result.CanUseToken)
|
||||
if (_settings.UseFreeleechToken == (int)OrpheusFreeleechTokenAction.Required && !result.CanUseToken)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var id = result.TorrentId;
|
||||
var infoUrl = GetInfoUrl(result.GroupId, id);
|
||||
var isFreeLeech = result.IsFreeLeech || result.IsNeutralLeech || result.IsPersonalFreeLeech;
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Guid = infoUrl,
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = GetDownloadUrl(id, !result.IsFreeLeech && !result.IsNeutralLeech && !result.IsPersonalFreeLeech),
|
||||
DownloadUrl = GetDownloadUrl(id, result.CanUseToken && !isFreeLeech),
|
||||
Title = WebUtility.HtmlDecode(result.GroupName),
|
||||
Size = long.Parse(result.Size),
|
||||
Seeders = int.Parse(result.Seeders),
|
||||
@@ -350,7 +347,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
PublishDate = long.TryParse(result.GroupTime, out var num) ? DateTimeOffset.FromUnixTimeSeconds(num).UtcDateTime : DateTimeUtil.FromFuzzyTime(result.GroupTime),
|
||||
Files = result.FileCount,
|
||||
Grabs = result.Snatches,
|
||||
DownloadVolumeFactor = result.IsFreeLeech || result.IsNeutralLeech || result.IsPersonalFreeLeech ? 0 : 1,
|
||||
DownloadVolumeFactor = isFreeLeech ? 0 : 1,
|
||||
UploadVolumeFactor = result.IsNeutralLeech ? 0 : 1
|
||||
};
|
||||
|
||||
@@ -416,7 +413,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
.AddQueryParam("id", torrentId);
|
||||
|
||||
// Orpheus fails to download if usetoken=0 so we need to only add if we will use one
|
||||
if (_settings.UseFreeleechToken && canUseToken)
|
||||
if (_settings.UseFreeleechToken is (int)OrpheusFreeleechTokenAction.Preferred or (int)OrpheusFreeleechTokenAction.Required && canUseToken)
|
||||
{
|
||||
url = url.AddQueryParam("usetoken", "1");
|
||||
}
|
||||
@@ -450,18 +447,30 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public OrpheusSettings()
|
||||
{
|
||||
Apikey = "";
|
||||
UseFreeleechToken = false;
|
||||
UseFreeleechToken = (int)OrpheusFreeleechTokenAction.Never;
|
||||
}
|
||||
|
||||
[FieldDefinition(2, Label = "ApiKey", HelpText = "IndexerOrpheusSettingsApiKeyHelpText", Privacy = PrivacyLevel.ApiKey)]
|
||||
public string Apikey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Use Freeleech Tokens", HelpText = "Use freeleech tokens when available", Type = FieldType.Checkbox)]
|
||||
public bool UseFreeleechToken { get; set; }
|
||||
[FieldDefinition(3, Type = FieldType.Select, Label = "Use Freeleech Tokens", SelectOptions = typeof(OrpheusFreeleechTokenAction), HelpText = "When to use freeleech tokens")]
|
||||
public int UseFreeleechToken { get; set; }
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
|
||||
public enum OrpheusFreeleechTokenAction
|
||||
{
|
||||
[FieldOption(Label = "Never", Hint = "Do not use tokens")]
|
||||
Never = 0,
|
||||
|
||||
[FieldOption(Label = "Preferred", Hint = "Use token if possible")]
|
||||
Preferred = 1,
|
||||
|
||||
[FieldOption(Label = "Required", Hint = "Abort download if unable to use token")]
|
||||
Required = 2,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,32 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
public override async Task<IndexerDownloadResponse> Download(Uri link)
|
||||
{
|
||||
var downloadResponse = await base.Download(link);
|
||||
|
||||
var fileData = downloadResponse.Data;
|
||||
|
||||
if (Settings.UseFreeleechToken == (int)RedactedFreeleechTokenAction.Preferred
|
||||
&& fileData.Length >= 1
|
||||
&& fileData[0] != 'd' // simple test for torrent vs HTML content
|
||||
&& link.Query.Contains("usetoken=1"))
|
||||
{
|
||||
var html = Encoding.GetString(fileData);
|
||||
|
||||
if (html.Contains("You do not have any freeleech tokens left.")
|
||||
|| html.Contains("You do not have enough freeleech tokens")
|
||||
|| html.Contains("This torrent is too large.")
|
||||
|| html.Contains("You cannot use tokens here"))
|
||||
{
|
||||
// Try to download again without usetoken
|
||||
downloadResponse = await base.Download(link.RemoveQueryParam("usetoken"));
|
||||
}
|
||||
}
|
||||
|
||||
return downloadResponse;
|
||||
}
|
||||
}
|
||||
|
||||
public class RedactedRequestGenerator : IIndexerRequestGenerator
|
||||
@@ -248,7 +274,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
foreach (var torrent in result.Torrents)
|
||||
{
|
||||
// skip releases that cannot be used with freeleech tokens when the option is enabled
|
||||
if (_settings.UseFreeleechToken && !torrent.CanUseToken)
|
||||
if (_settings.UseFreeleechToken == (int)RedactedFreeleechTokenAction.Required && !torrent.CanUseToken)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -263,12 +289,13 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
var title = GetTitle(result, torrent);
|
||||
var infoUrl = GetInfoUrl(result.GroupId, id);
|
||||
var isFreeLeech = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsFreeload || torrent.IsPersonalFreeLeech;
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Guid = infoUrl,
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = GetDownloadUrl(id, !torrent.IsFreeLeech && !torrent.IsNeutralLeech && !torrent.IsFreeload && !torrent.IsPersonalFreeLeech),
|
||||
DownloadUrl = GetDownloadUrl(id, torrent.CanUseToken && !isFreeLeech),
|
||||
Title = WebUtility.HtmlDecode(title),
|
||||
Artist = WebUtility.HtmlDecode(result.Artist),
|
||||
Album = WebUtility.HtmlDecode(result.GroupName),
|
||||
@@ -282,7 +309,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
Scene = torrent.Scene,
|
||||
Files = torrent.FileCount,
|
||||
Grabs = torrent.Snatches,
|
||||
DownloadVolumeFactor = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsFreeload || torrent.IsPersonalFreeLeech ? 0 : 1,
|
||||
DownloadVolumeFactor = isFreeLeech ? 0 : 1,
|
||||
UploadVolumeFactor = torrent.IsNeutralLeech || torrent.IsFreeload ? 0 : 1
|
||||
};
|
||||
|
||||
@@ -304,7 +331,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
else
|
||||
{
|
||||
// skip releases that cannot be used with freeleech tokens when the option is enabled
|
||||
if (_settings.UseFreeleechToken && !result.CanUseToken)
|
||||
if (_settings.UseFreeleechToken == (int)RedactedFreeleechTokenAction.Required && !result.CanUseToken)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -317,12 +344,13 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
var id = result.TorrentId;
|
||||
var infoUrl = GetInfoUrl(result.GroupId, id);
|
||||
var isFreeLeech = result.IsFreeLeech || result.IsNeutralLeech || result.IsFreeload || result.IsPersonalFreeLeech;
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Guid = infoUrl,
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = GetDownloadUrl(id, !result.IsFreeLeech && !result.IsNeutralLeech && !result.IsFreeload && !result.IsPersonalFreeLeech),
|
||||
DownloadUrl = GetDownloadUrl(id, result.CanUseToken && !isFreeLeech),
|
||||
Title = WebUtility.HtmlDecode(result.GroupName),
|
||||
Size = long.Parse(result.Size),
|
||||
Seeders = int.Parse(result.Seeders),
|
||||
@@ -330,7 +358,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
PublishDate = DateTimeOffset.FromUnixTimeSeconds(ParseUtil.CoerceLong(result.GroupTime)).UtcDateTime,
|
||||
Files = result.FileCount,
|
||||
Grabs = result.Snatches,
|
||||
DownloadVolumeFactor = result.IsFreeLeech || result.IsNeutralLeech || result.IsFreeload || result.IsPersonalFreeLeech ? 0 : 1,
|
||||
DownloadVolumeFactor = isFreeLeech ? 0 : 1,
|
||||
UploadVolumeFactor = result.IsNeutralLeech || result.IsFreeload ? 0 : 1
|
||||
};
|
||||
|
||||
@@ -395,7 +423,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
.AddQueryParam("action", "download")
|
||||
.AddQueryParam("id", torrentId);
|
||||
|
||||
if (_settings.UseFreeleechToken && canUseToken)
|
||||
if (_settings.UseFreeleechToken is (int)RedactedFreeleechTokenAction.Preferred or (int)RedactedFreeleechTokenAction.Required && canUseToken)
|
||||
{
|
||||
url = url.AddQueryParam("usetoken", "1");
|
||||
}
|
||||
@@ -429,14 +457,14 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public RedactedSettings()
|
||||
{
|
||||
Apikey = "";
|
||||
UseFreeleechToken = false;
|
||||
UseFreeleechToken = (int)RedactedFreeleechTokenAction.Never;
|
||||
}
|
||||
|
||||
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "IndexerRedactedSettingsApiKeyHelpText")]
|
||||
public string Apikey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Use Freeleech Tokens", Type = FieldType.Checkbox, HelpText = "Use freeleech tokens when available")]
|
||||
public bool UseFreeleechToken { get; set; }
|
||||
[FieldDefinition(3, Type = FieldType.Select, Label = "Use Freeleech Tokens", SelectOptions = typeof(RedactedFreeleechTokenAction), HelpText = "When to use freeleech tokens")]
|
||||
public int UseFreeleechToken { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Freeload Only", Type = FieldType.Checkbox, Advanced = true, HelpTextWarning = "Search freeload torrents only. End date: 31 January 2024, 23:59 UTC.")]
|
||||
public bool FreeloadOnly { get; set; }
|
||||
@@ -446,4 +474,16 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
|
||||
public enum RedactedFreeleechTokenAction
|
||||
{
|
||||
[FieldOption(Label = "Never", Hint = "Do not use tokens")]
|
||||
Never = 0,
|
||||
|
||||
[FieldOption(Label = "Preferred", Hint = "Use token if possible")]
|
||||
Preferred = 1,
|
||||
|
||||
[FieldOption(Label = "Required", Hint = "Abort download if unable to use token")]
|
||||
Required = 2,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ public class SecretCinemaParser : IParseIndexerResponse
|
||||
.AddQueryParam("action", "download")
|
||||
.AddQueryParam("id", torrentId);
|
||||
|
||||
if (_settings.UseFreeleechToken)
|
||||
if (_settings.UseFreeleechToken is (int)GazelleFreeleechTokenAction.Preferred or (int)GazelleFreeleechTokenAction.Required)
|
||||
{
|
||||
url = url.AddQueryParam("useToken", "1");
|
||||
}
|
||||
|
||||
@@ -607,12 +607,7 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
protected virtual bool CheckIfLoginNeeded(HttpResponse httpResponse)
|
||||
{
|
||||
if (httpResponse.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return httpResponse.StatusCode == HttpStatusCode.Unauthorized;
|
||||
}
|
||||
|
||||
protected virtual Task DoLogin()
|
||||
|
||||
@@ -318,5 +318,24 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
base.Update(definition);
|
||||
}
|
||||
|
||||
public override IEnumerable<IndexerDefinition> Update(IEnumerable<IndexerDefinition> definitions)
|
||||
{
|
||||
var indexerDefinitions = definitions.ToList();
|
||||
|
||||
foreach (var definition in indexerDefinitions)
|
||||
{
|
||||
var provider = _providers.First(v => v.GetType().Name == definition.Implementation);
|
||||
|
||||
SetProviderCharacteristics(provider, definition);
|
||||
|
||||
if (definition.Implementation == nameof(Cardigann))
|
||||
{
|
||||
MapCardigannDefinition(definition);
|
||||
}
|
||||
}
|
||||
|
||||
return base.Update(indexerDefinitions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -771,5 +771,7 @@
|
||||
"AverageGrabs": "Promedio de capturas",
|
||||
"AllSearchResultsHiddenByFilter": "Todos los resultados están ocultos por el filtro aplicado.",
|
||||
"PackageVersionInfo": "{packageVersion} por {packageAuthor}",
|
||||
"HealthMessagesInfoBox": "Puede encontrar más información sobre la causa de estos mensajes de comprobación de salud haciendo clic en el enlace wiki (icono de libro) al final de la fila, o comprobando sus [logs]({link}). Si tienes dificultades para interpretar estos mensajes, puedes ponerte en contacto con nuestro servicio de asistencia en los enlaces que aparecen a continuación."
|
||||
"HealthMessagesInfoBox": "Puede encontrar más información sobre la causa de estos mensajes de comprobación de salud haciendo clic en el enlace wiki (icono de libro) al final de la fila, o comprobando sus [logs]({link}). Si tienes dificultades para interpretar estos mensajes, puedes ponerte en contacto con nuestro servicio de asistencia en los enlaces que aparecen a continuación.",
|
||||
"LogSizeLimit": "Límite de tamaño de registro",
|
||||
"LogSizeLimitHelpText": "Máximo tamaño de archivo de registro en MB antes de archivarlo. Predeterminado es 1MB."
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"Proxy": "Proxy",
|
||||
"Protocol": "Protocole",
|
||||
"Options": "Options",
|
||||
"NoChanges": "Aucuns changements",
|
||||
"NoChanges": "Aucun changement",
|
||||
"NoChange": "Pas de changement",
|
||||
"MoreInfo": "Plus d'informations",
|
||||
"Grabbed": "Saisie",
|
||||
@@ -509,7 +509,7 @@
|
||||
"DeleteSelectedDownloadClients": "Supprimer le(s) client(s) de téléchargement",
|
||||
"DeleteSelectedDownloadClientsMessageText": "Voulez-vous vraiment supprimer {count} client(s) de téléchargement sélectionné(s) ?",
|
||||
"StopSelecting": "Effacer la sélection",
|
||||
"UpdateAvailableHealthCheckMessage": "Une nouvelle mise à jour est disponible",
|
||||
"UpdateAvailableHealthCheckMessage": "Une nouvelle mise à jour est disponible : {version}",
|
||||
"AdvancedSettingsHiddenClickToShow": "Paramètres avancés masqués, cliquez pour afficher",
|
||||
"AdvancedSettingsShownClickToHide": "Paramètres avancés affichés, cliquez pour masquer",
|
||||
"AppsMinimumSeeders": "Apps avec le nombre minimum de seeders disponibles",
|
||||
@@ -644,7 +644,7 @@
|
||||
"IndexerSettingsQueryLimitHelpText": "Le nombre de requêtes maximales tel que spécifié par l'unité respective que {appName} autorisera au site",
|
||||
"IndexerSettingsRssKey": "Clé RSS",
|
||||
"IndexerSettingsSeedRatioHelpText": "Le ratio qu'un torrent doit atteindre avant de s'arrêter, vide utilise la valeur par défaut du client de téléchargement. Le ratio doit être d'au moins 1.0 et suivre les règles des indexeurs",
|
||||
"IndexerSettingsSeedTime": "Temps d'envoie",
|
||||
"IndexerSettingsSeedTime": "Temps d'envoi",
|
||||
"IndexerTorrentSyndikatSettingsApiKeyHelpText": "Clé API du site",
|
||||
"BlackholeFolderHelpText": "Dossier dans lequel {appName} stockera le fichier {extension}",
|
||||
"DefaultCategory": "Catégorie par défaut",
|
||||
@@ -692,7 +692,7 @@
|
||||
"IndexerSettingsCookie": "Cookie",
|
||||
"IndexerSettingsCookieHelpText": "Cookie du site",
|
||||
"IndexerSettingsSeedTimeHelpText": "Durée pendant laquelle un torrent doit être envoyé avant de s'arrêter, vide utilise la valeur par défaut du client de téléchargement",
|
||||
"IndexerSettingsSeedRatio": "Ratio d'envoie",
|
||||
"IndexerSettingsSeedRatio": "Ratio d'envoi",
|
||||
"IndexerSettingsVipExpiration": "Expiration de la carte VIP",
|
||||
"SecretToken": "Jeton secret",
|
||||
"TorrentBlackholeSaveMagnetFiles": "Enregistrer les fichiers magnétiques",
|
||||
@@ -771,5 +771,7 @@
|
||||
"AverageGrabs": "Prises moyennes",
|
||||
"AverageQueries": "Requêtes moyennes",
|
||||
"PackageVersionInfo": "{packageVersion} par {packageAuthor}",
|
||||
"HealthMessagesInfoBox": "Vous pouvez trouver plus d'informations sur la cause de ces messages de contrôle de santé en cliquant sur le lien wiki (icône de livre) à la fin de la ligne, ou en vérifiant vos [journaux]({link}). Si vous rencontrez des difficultés pour interpréter ces messages, vous pouvez contacter notre support, via les liens ci-dessous."
|
||||
"HealthMessagesInfoBox": "Vous pouvez trouver plus d'informations sur la cause de ces messages de contrôle de santé en cliquant sur le lien wiki (icône de livre) à la fin de la ligne, ou en vérifiant vos [journaux]({link}). Si vous rencontrez des difficultés pour interpréter ces messages, vous pouvez contacter notre support, via les liens ci-dessous.",
|
||||
"LogSizeLimit": "Limite de taille du journal",
|
||||
"LogSizeLimitHelpText": "Taille maximale du fichier journal en Mo avant archivage. La valeur par défaut est de 1 Mo."
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
"ConnectionLost": "Connessione Persa",
|
||||
"Component": "Componente",
|
||||
"Columns": "Colonne",
|
||||
"DeleteBackupMessageText": "Sei sicuro di voler cancellare il backup '{name}'?",
|
||||
"DeleteBackupMessageText": "Sei sicuro di voler eliminare il backup '{name}'?",
|
||||
"CancelPendingTask": "Sei sicuro di voler cancellare questa operazione in sospeso?",
|
||||
"BranchUpdateMechanism": "Ramo utilizzato dal sistema di aggiornamento esterno",
|
||||
"BranchUpdate": "Branca da usare per aggiornare {appName}",
|
||||
|
||||
@@ -498,5 +498,7 @@
|
||||
"Script": "Script",
|
||||
"PublishedDate": "Publicatie Datum",
|
||||
"Redirected": "Omleiden",
|
||||
"AllSearchResultsHiddenByFilter": "Alle resultaten zijn verborgen door het toegepaste filter"
|
||||
"AllSearchResultsHiddenByFilter": "Alle resultaten zijn verborgen door het toegepaste filter",
|
||||
"Clone": "Kloon",
|
||||
"DownloadClientSettingsUrlBaseHelpText": "Voegt een voorvoegsel toe aan de {connectionName} url, zoals {url}"
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
"AuthenticationMethodHelpText": "Wymagaj nazwy użytkownika i hasła, aby uzyskać dostęp do {appName}",
|
||||
"BackupFolderHelpText": "Względne ścieżki będą znajdować się w katalogu AppData {appName}",
|
||||
"BackupRetentionHelpText": "Automatyczne kopie zapasowe starsze niż okres przechowywania zostaną automatycznie wyczyszczone",
|
||||
"BindAddressHelpText": "Prawidłowy adres IP4 lub „*” dla wszystkich interfejsów",
|
||||
"BindAddressHelpText": "Prawidłowy adres IP, localhost lub '*' dla wszystkich interfejsów",
|
||||
"BranchUpdateMechanism": "Gałąź używana przez zewnętrzny mechanizm aktualizacji",
|
||||
"BypassProxyForLocalAddresses": "Pomijaj serwer proxy dla adresów lokalnych",
|
||||
"CancelPendingTask": "Czy na pewno chcesz anulować to oczekujące zadanie?",
|
||||
@@ -124,9 +124,9 @@
|
||||
"DatabaseMigration": "Migracja bazy danych",
|
||||
"DeleteApplicationMessageText": "Czy na pewno chcesz usunąć powiadomienie „{0}”?",
|
||||
"DeleteBackup": "Usuń kopię zapasową",
|
||||
"DeleteBackupMessageText": "Czy na pewno chcesz usunąć kopię zapasową „{0}”?",
|
||||
"DeleteBackupMessageText": "Czy na pewno chcesz usunąć kopię zapasową „{name}”?",
|
||||
"DeleteDownloadClient": "Usuń klienta pobierania",
|
||||
"DeleteDownloadClientMessageText": "Czy na pewno chcesz usunąć klienta pobierania „{0}”?",
|
||||
"DeleteDownloadClientMessageText": "Czy na pewno chcesz usunąć klienta pobierania „{name}”?",
|
||||
"DeleteNotification": "Usuń powiadomienie",
|
||||
"Disabled": "Wyłączone",
|
||||
"Docker": "Doker",
|
||||
@@ -237,7 +237,7 @@
|
||||
"SSLCertPathHelpText": "Ścieżka do pliku pfx",
|
||||
"SSLPort": "Port SSL",
|
||||
"StartTypingOrSelectAPathBelow": "Zacznij pisać lub wybierz ścieżkę poniżej",
|
||||
"StartupDirectory": "Katalog startowy",
|
||||
"StartupDirectory": "Katalog Startowy",
|
||||
"Status": "Status",
|
||||
"SuggestTranslationChange": "Zaproponuj zmianę tłumaczenia",
|
||||
"SystemTimeCheckMessage": "Czas systemowy jest wyłączony o więcej niż 1 dzień. Zaplanowane zadania mogą nie działać poprawnie, dopóki czas nie zostanie skorygowany",
|
||||
@@ -382,7 +382,7 @@
|
||||
"EditDownloadClientImplementation": "Dodaj klienta pobierania - {implementationName}",
|
||||
"Id": "Identyfikator",
|
||||
"AddApplicationImplementation": "Dodaj Connection - {implementationName}",
|
||||
"AddIndexerImplementation": "Dodaj condition - {implementationName}",
|
||||
"AddIndexerImplementation": "Dodaj indeks - {implementationName}",
|
||||
"AddIndexerProxyImplementation": "Dodaj condition - {implementationName}",
|
||||
"EditConnectionImplementation": "Dodaj Connection - {implementationName}",
|
||||
"EditApplicationImplementation": "Dodaj Connection - {implementationName}",
|
||||
@@ -397,5 +397,23 @@
|
||||
"Script": "Scenariusz",
|
||||
"BuiltIn": "Wbudowany",
|
||||
"PublishedDate": "Data publikacji",
|
||||
"AllSearchResultsHiddenByFilter": "Wszystkie wyniki są ukrywane przez zastosowany filtr"
|
||||
"AllSearchResultsHiddenByFilter": "Wszystkie wyniki są ukrywane przez zastosowany filtr",
|
||||
"AppUpdated": "{appName} Zaktualizowany",
|
||||
"AppUpdatedVersion": "{appName} został zaktualizowany do wersji `{version}`, by uzyskać nowe zmiany należy przeładować {appName}",
|
||||
"AddCustomFilter": "Dodaj niestandardowy filtr",
|
||||
"AuthenticationMethodHelpTextWarning": "Wybierz prawidłową metodę autoryzacji",
|
||||
"Any": "Dowolny",
|
||||
"AuthenticationMethod": "Metoda Autoryzacji",
|
||||
"AuthenticationRequired": "Wymagana Autoryzacja",
|
||||
"Categories": "Kategorie",
|
||||
"Label": "Etykieta",
|
||||
"Notification": "Powiadomienia",
|
||||
"Season": "Sezon",
|
||||
"Theme": "Motyw",
|
||||
"Artist": "artysta",
|
||||
"Album": "album",
|
||||
"Connect": "Powiadomienia",
|
||||
"Episode": "odcinek",
|
||||
"Notifications": "Powiadomienia",
|
||||
"Publisher": "Wydawca"
|
||||
}
|
||||
|
||||
@@ -771,5 +771,7 @@
|
||||
"AverageGrabs": "Média de Capturas",
|
||||
"AllSearchResultsHiddenByFilter": "Todos os resultados da pesquisa são ocultados pelo filtro aplicado.",
|
||||
"PackageVersionInfo": "{packageVersion} por {packageAuthor}",
|
||||
"HealthMessagesInfoBox": "Para saber mais sobre a causa dessas mensagens de verificação de integridade, clique no link da wiki (ícone de livro) no final da linha ou verifique os [logs]({link}). Se tiver dificuldade em interpretar essas mensagens, entre em contato com nosso suporte nos links abaixo."
|
||||
"HealthMessagesInfoBox": "Para saber mais sobre a causa dessas mensagens de verificação de integridade, clique no link da wiki (ícone de livro) no final da linha ou verifique os [logs]({link}). Se tiver dificuldade em interpretar essas mensagens, entre em contato com nosso suporte nos links abaixo.",
|
||||
"LogSizeLimit": "Limite de Tamanho do Registro",
|
||||
"LogSizeLimitHelpText": "Tamanho máximo do arquivo de registro em MB antes do arquivamento. O padrão é 1 MB."
|
||||
}
|
||||
|
||||
@@ -8,16 +8,16 @@
|
||||
"BackupIntervalHelpText": "Периодичность автоматического резервного копирования",
|
||||
"Backup": "Резервное копирование",
|
||||
"AutomaticSearch": "Автоматический поиск",
|
||||
"Authentication": "Аутентификация",
|
||||
"ApplyTags": "Применить тэги",
|
||||
"Authentication": "Авторизация",
|
||||
"ApplyTags": "Применить теги",
|
||||
"Apply": "Применить",
|
||||
"AppDataLocationHealthCheckMessage": "Обновление будет не возможно, во избежание удаления данных программы во время обновления",
|
||||
"AppDataLocationHealthCheckMessage": "Обновление не позволит сохранить AppData при обновлении",
|
||||
"ApiKey": "API ключ",
|
||||
"Analytics": "Аналитика",
|
||||
"AddIndexer": "Добавить индексер",
|
||||
"AddIndexer": "Добавить индексатор",
|
||||
"Added": "Добавлено",
|
||||
"Actions": "Действия",
|
||||
"About": "Об",
|
||||
"About": "О программе",
|
||||
"DeleteBackupMessageText": "Вы уверены, что хотите удалить резервную копию '{name}'?",
|
||||
"DeleteBackup": "Удалить резервную копию",
|
||||
"Delete": "Удалить",
|
||||
@@ -27,41 +27,41 @@
|
||||
"Rss": "RSS",
|
||||
"SendAnonymousUsageData": "Отправка анонимных данных об использовании",
|
||||
"UnableToLoadHistory": "Не удалось загрузить историю",
|
||||
"MoreInfo": "Ещё инфо",
|
||||
"MovieIndexScrollTop": "Индекс фильма: промотать вверх",
|
||||
"MoreInfo": "Больше информации",
|
||||
"MovieIndexScrollTop": "Индекс фильма: Прокрутить вверх",
|
||||
"Size": "Размер",
|
||||
"OpenBrowserOnStart": "Открывать браузер при запуске",
|
||||
"OpenBrowserOnStart": "Открыть браузер при запуске",
|
||||
"OpenThisModal": "Открыть это модальное окно",
|
||||
"Options": "Опции",
|
||||
"Options": "Параметры",
|
||||
"PackageVersion": "Версия пакета",
|
||||
"PageSize": "Размер страницы",
|
||||
"PageSizeHelpText": "Количество показываемое на каждой страницы",
|
||||
"PageSizeHelpText": "Количество элементов, отображаемых на каждой странице",
|
||||
"Password": "Пароль",
|
||||
"Peers": "Пиры",
|
||||
"PendingChangesDiscardChanges": "Не применять изменения и выйти",
|
||||
"PendingChangesStayReview": "Оставайтесь и просмотрите изменения",
|
||||
"PendingChangesDiscardChanges": "Отменить изменения и выйти",
|
||||
"PendingChangesStayReview": "Остаться и просмотреть изменения",
|
||||
"Port": "Порт",
|
||||
"PortNumber": "Номер порта",
|
||||
"LogFiles": "Файлы журнала",
|
||||
"Details": "Подробности",
|
||||
"DownloadClients": "Клиенты для скачивания",
|
||||
"DownloadClients": "Клиенты загрузки",
|
||||
"Filename": "Имя файла",
|
||||
"Files": "Файлы",
|
||||
"Filter": "Фильтр",
|
||||
"IndexerStatusAllUnavailableHealthCheckMessage": "Все индексаторы недоступны из-за ошибок",
|
||||
"LogLevel": "Уровень журнала",
|
||||
"LogLevelTraceHelpTextWarning": "Отслеживание журнала следует включать только временно",
|
||||
"LogLevel": "Уровень журналирования",
|
||||
"LogLevelTraceHelpTextWarning": "Включение трассировки журнала должно быть временным",
|
||||
"Logs": "Журналы",
|
||||
"Manual": "Ручной",
|
||||
"Mechanism": "Механизм",
|
||||
"NoBackupsAreAvailable": "Нет резервных копий",
|
||||
"DeleteDownloadClientMessageText": "Вы уверены, что хотите удалить клиент загрузки '{name}'?",
|
||||
"Edit": "Редактирование",
|
||||
"Edit": "Редактировать",
|
||||
"Enable": "Включить",
|
||||
"Indexer": "Индексатор",
|
||||
"SettingsShortDateFormat": "Короткий формат даты",
|
||||
"SettingsShowRelativeDates": "Показать относительные даты",
|
||||
"SettingsShowRelativeDatesHelpText": "Показывать относительные (сегодня / вчера / и т. д.) или абсолютные даты",
|
||||
"SettingsShowRelativeDatesHelpText": "Показывать относительные (Сегодня/Вчера/и т.п.) или абсолютные даты",
|
||||
"Docker": "Docker",
|
||||
"FocusSearchBox": "Поле поиска в фокусе",
|
||||
"Reset": "Сброс",
|
||||
@@ -73,26 +73,26 @@
|
||||
"Usenet": "Usenet",
|
||||
"AcceptConfirmationModal": "Окно подтверждения",
|
||||
"Today": "Сегодня",
|
||||
"DeleteIndexerProxyMessageText": "Вы уверены, что хотите удалить тэг '{0}'?",
|
||||
"ExistingTag": "Существующий тэг",
|
||||
"DeleteIndexerProxyMessageText": "Вы уверены, что хотите удалить прокси индексатора '{name}'?",
|
||||
"ExistingTag": "Существующий тег",
|
||||
"Failed": "Неудачно",
|
||||
"IndexerProxyStatusUnavailableHealthCheckMessage": "Индексаторы недоступны из-за ошибок: {indexerProxyNames}",
|
||||
"IndexerProxyStatusUnavailableHealthCheckMessage": "Прокси индексатора недоступны из-за ошибок: {indexerProxyNames}",
|
||||
"Indexers": "Индексаторы",
|
||||
"InteractiveSearch": "Интерактивный поиск",
|
||||
"Interval": "Интервал",
|
||||
"KeyboardShortcuts": "Горячие клавиши",
|
||||
"Language": "Язык",
|
||||
"LastWriteTime": "Последнее время записи",
|
||||
"LaunchBrowserHelpText": " Открывать браузер и переходить на страницу {appName} при запуске программы.",
|
||||
"LaunchBrowserHelpText": " Открыть браузер и перейти на страницу {appName} при запуске приложения.",
|
||||
"Level": "Уровень",
|
||||
"Ok": "Хорошо",
|
||||
"AddDownloadClient": "Добавить программу для скачивания",
|
||||
"UpdateMechanismHelpText": "Используйте встроенную в {appName} функцию обновления или скрипт",
|
||||
"Ok": "Ок",
|
||||
"AddDownloadClient": "Добавить клиент загрузки",
|
||||
"UpdateMechanismHelpText": "Использовать встроенный инструмент обновления {appName} или скрипт.",
|
||||
"IndexerStatusUnavailableHealthCheckMessage": "Индексаторы недоступны из-за ошибок: {indexerNames}",
|
||||
"NoTagsHaveBeenAddedYet": "Теги еще не добавлены",
|
||||
"UnableToLoadTags": "Невозможно загрузить теги",
|
||||
"AnalyticsEnabledHelpText": "Отправлять в {appName} анонимную информацию об использовании и ошибках. Анонимная статистика включает в себя информацию о браузере, какие страницы веб-интерфейса {appName} загружены, сообщения об ошибках, а также операционной системе. Мы используем эту информацию для выявления ошибок, а также для разработки нового функционала.",
|
||||
"AuthenticationMethodHelpText": "Необходим логин и пароль для доступа в {appName}",
|
||||
"NoTagsHaveBeenAddedYet": "Теги ещё не добавлены",
|
||||
"UnableToLoadTags": "Не удалось загрузить теги",
|
||||
"AnalyticsEnabledHelpText": "Отправлять анонимную информацию об использовании и ошибках на серверы {appName}. Анонимная статистика включает в себя информацию о вашем браузере, какие страницы веб-интерфейса {appName} вы используете, отчёты об ошибках, а также версию операционной системы и среды выполнения. Мы будем использовать эту информацию для разработки нового функционала и исправления ошибок.",
|
||||
"AuthenticationMethodHelpText": "Необходимо ввести имя пользователя и пароль для доступа к {appName}",
|
||||
"BackupFolderHelpText": "Относительные пути будут в каталоге AppData {appName}",
|
||||
"BeforeUpdate": "До обновления",
|
||||
"BindAddress": "Привязать адрес",
|
||||
@@ -101,8 +101,8 @@
|
||||
"BranchUpdate": "Ветвь для обновления {appName}",
|
||||
"BranchUpdateMechanism": "Ветвь, используемая внешним механизмом обновления",
|
||||
"BypassProxyForLocalAddresses": "Обход прокси для локальных адресов",
|
||||
"Cancel": "Отменить",
|
||||
"CancelPendingTask": "Вы уверены, что хотите убрать данную задачу из очереди?",
|
||||
"Cancel": "Отмена",
|
||||
"CancelPendingTask": "Вы уверены, что хотите отменить эту ожидающую задачу?",
|
||||
"CertificateValidation": "Проверка сертификата",
|
||||
"CertificateValidationHelpText": "Изменить строгое подтверждение сертификации НТТР",
|
||||
"ChangeHasNotBeenSavedYet": "Изменения ещё не вступили в силу",
|
||||
@@ -113,37 +113,37 @@
|
||||
"ConnectionLost": "Соединение прервано",
|
||||
"Connections": "Соединения",
|
||||
"ConnectSettings": "Настройки соединения",
|
||||
"CouldNotConnectSignalR": "Не возможно подключиться к SignalR, интерфейс не будет обновляться",
|
||||
"CouldNotConnectSignalR": "Не удалось подключиться к SignalR, интерфейс не будет обновляться",
|
||||
"Custom": "Настраиваемый",
|
||||
"CustomFilters": "Настраиваемые фильтры",
|
||||
"Date": "Дата",
|
||||
"Dates": "Даты",
|
||||
"DatabaseMigration": "Перенос БД",
|
||||
"DatabaseMigration": "Миграция базы данных",
|
||||
"DeleteNotification": "Удалить уведомление",
|
||||
"DeleteNotificationMessageText": "Вы уверены, что хотите удалить уведомление '{name}'?",
|
||||
"DeleteTag": "Удалить тэг",
|
||||
"DeleteTagMessageText": "Вы уверены, что хотите удалить тэг '{label}'?",
|
||||
"DeleteTag": "Удалить тег",
|
||||
"DeleteTagMessageText": "Вы уверены, что хотите удалить тег '{label}'?",
|
||||
"Disabled": "Выключено",
|
||||
"Discord": "Discord",
|
||||
"Donations": "Пожертвования",
|
||||
"DownloadClient": "Загрузочный клиент",
|
||||
"DownloadClientSettings": "Настройки клиента скачиваний",
|
||||
"DownloadClientStatusAllClientHealthCheckMessage": "Все клиенты для скачивания недоступны из-за ошибок",
|
||||
"DownloadClientStatusSingleClientHealthCheckMessage": "Клиенты для скачивания недоступны из-за ошибок: {downloadClientNames}",
|
||||
"DownloadClient": "Клиент загрузки",
|
||||
"DownloadClientSettings": "Настройки клиента загрузки",
|
||||
"DownloadClientStatusAllClientHealthCheckMessage": "Все клиенты загрузок недоступны из-за ошибок",
|
||||
"DownloadClientStatusSingleClientHealthCheckMessage": "Клиенты загрузок недоступны из-за ошибок: {downloadClientNames}",
|
||||
"EnableAutomaticSearch": "Включить автоматический поиск",
|
||||
"EnableAutomaticSearchHelpText": "Будет использовано для автоматических поисков через интерфейс или {appName}",
|
||||
"EnableAutomaticSearchHelpText": "Будет использовано при автоматическом поиске через интерфейс или {appName}",
|
||||
"Enabled": "Включено",
|
||||
"EnableInteractiveSearch": "Включить интерактивный поиск",
|
||||
"EnableRss": "Включить RSS",
|
||||
"Fixed": "Исправлено",
|
||||
"Folder": "Папка",
|
||||
"ForMoreInformationOnTheIndividualDownloadClients": "Для дополнительной информации пл программам скачивания нажмите эту кнопку.",
|
||||
"General": "Основное",
|
||||
"GeneralSettings": "Основные настройки",
|
||||
"GeneralSettingsSummary": "Порт, SSL, логин/пароль, прокси, аналитика и обновления",
|
||||
"Folder": "Каталог",
|
||||
"ForMoreInformationOnTheIndividualDownloadClients": "Для получения дополнительной информации о каждом клиенте загрузки нажмите на кнопки информации.",
|
||||
"General": "Общие",
|
||||
"GeneralSettings": "Общие настройки",
|
||||
"GeneralSettingsSummary": "Порт, SSL, имя пользователя/пароль, прокси-сервер, аналитика и обновления",
|
||||
"Grabbed": "Захвачено",
|
||||
"Grabs": "Захватить",
|
||||
"Health": "Здоровье",
|
||||
"Grabs": "Захваты",
|
||||
"Health": "Состояние",
|
||||
"NoIssuesWithYourConfiguration": "С вашей конфигурацией нет проблем",
|
||||
"HideAdvanced": "Скрыть расширенные",
|
||||
"History": "История",
|
||||
@@ -152,40 +152,40 @@
|
||||
"Hostname": "Имя хоста",
|
||||
"IgnoredAddresses": "Игнорируемые адреса",
|
||||
"IllRestartLater": "Перезапущу позднее",
|
||||
"IncludeHealthWarningsHelpText": "Включая предупреждения о здоровье",
|
||||
"IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Все индексаторы недоступны из-за ошибок за последние 6 часов",
|
||||
"IndexerLongTermStatusUnavailableHealthCheckMessage": "Все индексаторы недоступны из-за ошибок за последние 6 часов: {indexerNames}",
|
||||
"IncludeHealthWarningsHelpText": "Включая предупреждения о состоянии",
|
||||
"IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Все индексаторы недоступны из-за ошибок более 6 часов",
|
||||
"IndexerLongTermStatusUnavailableHealthCheckMessage": "Все индексаторы недоступны из-за ошибок более 6 часов: {indexerNames}",
|
||||
"IndexerPriority": "Приоритет индексатора",
|
||||
"IndexerPriorityHelpText": "Приоритет индексаторов от 1 (наивысший) до 50 (низший). По-умолчанию: 25.",
|
||||
"IndexerProxyStatusAllUnavailableHealthCheckMessage": "Все индексаторы недоступны из-за ошибок",
|
||||
"IndexerPriorityHelpText": "Приоритет индексатора от 1 (Высший) до 50 (Низший). По умолчанию 25.",
|
||||
"IndexerProxyStatusAllUnavailableHealthCheckMessage": "Все прокси индексатора недоступны из-за ошибок",
|
||||
"Info": "Информация",
|
||||
"Logging": "Ведение журнала",
|
||||
"Logging": "Журналирование",
|
||||
"Message": "Сообщение",
|
||||
"MIA": "MIA",
|
||||
"Mode": "Режим",
|
||||
"MovieIndexScrollBottom": "Индекс фильма: промотать в низ",
|
||||
"MovieIndexScrollBottom": "Индекс фильма: Прокрутить вниз",
|
||||
"Name": "Имя",
|
||||
"New": "Новый",
|
||||
"NoChanges": "Нет изменений",
|
||||
"NoLeaveIt": "Нет, оставить",
|
||||
"NoLogFiles": "Нет файлов журнала",
|
||||
"NoUpdatesAreAvailable": "Нет обновлений",
|
||||
"OAuthPopupMessage": "Ваш браузер блокирует всплывающие окна",
|
||||
"OnHealthIssueHelpText": "По вопросам здоровья",
|
||||
"NoLogFiles": "Файлов журнала нет",
|
||||
"NoUpdatesAreAvailable": "Обновления отсутствуют",
|
||||
"OAuthPopupMessage": "Всплывающие окна блокируются вашим браузером",
|
||||
"OnHealthIssueHelpText": "При проблемах с состоянием",
|
||||
"PendingChangesMessage": "У вас есть несохраненные изменения. Вы уверены, что хотите покинуть эту страницу?",
|
||||
"Presets": "Пресеты",
|
||||
"Protocol": "Протокол",
|
||||
"Proxy": "Прокси",
|
||||
"ProxyBypassFilterHelpText": "Используйте ',' в качестве разделителя и '*.' как подстановочный знак для субдоменов",
|
||||
"ProxyBadRequestHealthCheckMessage": "Не удалось проверить прокси. Код: {statusCode}",
|
||||
"ProxyFailedToTestHealthCheckMessage": "Не удалось проверить прокси: {url}",
|
||||
"ProxyBypassFilterHelpText": "Используйте ',' как разделитель и '*.' как подстановочный знак для поддоменов",
|
||||
"ProxyBadRequestHealthCheckMessage": "Не удалось протестировать прокси. Код состояния: {statusCode}",
|
||||
"ProxyFailedToTestHealthCheckMessage": "Не удалось протестировать прокси: {url}",
|
||||
"ProxyResolveIpHealthCheckMessage": "Не удалось определить IP-адрес настроенного прокси-хоста {proxyHostName}",
|
||||
"ProxyPasswordHelpText": "Вам нужно только ввести имя пользователя и пароль, если они необходимы. В противном случае оставьте их пустыми.",
|
||||
"ProxyPasswordHelpText": "Вы должны указать имя пользователя и пароль только если они необходимы. В противном случае оставьте эти поля пустыми.",
|
||||
"ProxyType": "Тип прокси",
|
||||
"ProxyUsernameHelpText": "Вам нужно только ввести имя пользователя и пароль, если они необходимы. В противном случае оставьте их пустыми.",
|
||||
"ProxyUsernameHelpText": "Вы должны указать имя пользователя и пароль только если они необходимы. В противном случае оставьте эти поля пустыми.",
|
||||
"Queue": "Очередь",
|
||||
"ReadTheWikiForMoreInformation": "Прочтите Wiki для получения дополнительной информации",
|
||||
"ReleaseBranchCheckOfficialBranchMessage": "Ветка {0} не является допустимой веткой выпуска {appName}, вы не будете получать обновления",
|
||||
"ReleaseBranchCheckOfficialBranchMessage": "Ветвь {0} не является действительной релизной ветвью {appName}, вы не будете получать обновления",
|
||||
"ReleaseStatus": "Статус релиза",
|
||||
"Reload": "Перезагрузить",
|
||||
"RemovedFromTaskQueue": "Удалено из очереди задач",
|
||||
@@ -196,7 +196,7 @@
|
||||
"RestartNow": "Перезапустить сейчас",
|
||||
"RestartRequiredHelpTextWarning": "Для применения изменений, требуется перезапуск",
|
||||
"Restore": "Восстановить",
|
||||
"RestoreBackup": "Восстановить из резервной копии",
|
||||
"RestoreBackup": "Восстановить резервную копию",
|
||||
"Result": "Результат",
|
||||
"Retention": "Удержание",
|
||||
"RssIsNotSupportedWithThisIndexer": "RSS не поддерживается этим индексатором",
|
||||
@@ -207,148 +207,148 @@
|
||||
"ScriptPath": "Путь к скрипту",
|
||||
"Search": "Поиск",
|
||||
"Security": "Безопасность",
|
||||
"Seeders": "Сиды",
|
||||
"Seeders": "Сидеры",
|
||||
"SelectAll": "Выбрать все",
|
||||
"SetTags": "Установить теги",
|
||||
"Settings": "Настройки",
|
||||
"SettingsEnableColorImpairedMode": "Версия для слабовидящих",
|
||||
"SettingsEnableColorImpairedModeHelpText": "Стиль изменён чтобы слабовидящие лучше различали цвета",
|
||||
"SettingsLongDateFormat": "Длинный формат даты",
|
||||
"SettingsEnableColorImpairedMode": "Включить режим для людей с ограниченным цветовым восприятием",
|
||||
"SettingsEnableColorImpairedModeHelpText": "Изменённый стиль позволяет пользователям с нарушениями цветового восприятия лучше различать цветовую информацию",
|
||||
"SettingsLongDateFormat": "Расширенный формат даты",
|
||||
"SettingsTimeFormat": "Формат времени",
|
||||
"ShowAdvanced": "Показать расширенные",
|
||||
"ShowSearch": "Показать поиск",
|
||||
"ShowSearchHelpText": "Показать копку поиска по наведению",
|
||||
"ShowSearchHelpText": "Показать копку поиска при наведении",
|
||||
"Shutdown": "Выключить",
|
||||
"Sort": "Сортировка",
|
||||
"Source": "Источник",
|
||||
"SSLCertPassword": "Пароль SSL сертификата",
|
||||
"SSLCertPasswordHelpText": "Пароль pfx файла",
|
||||
"SSLCertPath": "Путь SSL сертификата",
|
||||
"SSLCertPathHelpText": "Путь к pfx файлу",
|
||||
"Source": "Исходный код",
|
||||
"SSLCertPassword": "Пароль сертификата SSL",
|
||||
"SSLCertPasswordHelpText": "Пароль файла pfx",
|
||||
"SSLCertPath": "Путь к сертификату SSL",
|
||||
"SSLCertPathHelpText": "Путь к файлу pfx",
|
||||
"StartTypingOrSelectAPathBelow": "Начните вводить или выберите путь ниже",
|
||||
"StartupDirectory": "Каталог автозагрузки",
|
||||
"StartupDirectory": "Каталог автозапуска",
|
||||
"Status": "Статус",
|
||||
"Style": "Стиль",
|
||||
"SuggestTranslationChange": "Предложить изменение перевода",
|
||||
"SystemTimeCheckMessage": "Расхождение системного времени более чем на 1 день. Запланированные задачи могут работать некорректно, пока не будет исправлено время",
|
||||
"TableOptionsColumnsMessage": "Выберите, какие столбцы отображаются и в каком порядке",
|
||||
"TagIsNotUsedAndCanBeDeleted": "Тег не используется и может быть удален",
|
||||
"TagsHelpText": "Применимо к фильмам с хотя бы одним подходящим тегом",
|
||||
"SystemTimeCheckMessage": "Системное время отклонилось более чем на 1 день. Запланированные задания могут не работать правильно, пока время не будет исправлено",
|
||||
"TableOptionsColumnsMessage": "Выберите видимые столбцы и порядок их отображения",
|
||||
"TagIsNotUsedAndCanBeDeleted": "Тег не используется и может быть удалён",
|
||||
"TagsHelpText": "Применяется к индексаторам, имеющим хотя бы один совпадающий тег",
|
||||
"TagsSettingsSummary": "Посмотрите все теги и способы их использования. Неиспользуемые теги можно удалить",
|
||||
"Tasks": "Задачи",
|
||||
"Test": "Тест",
|
||||
"TestAll": "Тестировать все",
|
||||
"TestAllClients": "Тестировать всех клиентов",
|
||||
"TestAllClients": "Тестировать все клиенты",
|
||||
"Time": "Время",
|
||||
"Title": "Название",
|
||||
"Tomorrow": "Завтра",
|
||||
"Torrent": "Торренты",
|
||||
"Torrent": "Торрент",
|
||||
"Torrents": "Торренты",
|
||||
"Type": "Тип",
|
||||
"UI": "Пользовательский интерфейс",
|
||||
"UILanguage": "Язык пользовательского интерфейса",
|
||||
"UILanguageHelpTextWarning": "Требуется перезагрузка браузера",
|
||||
"UISettings": "Настройки пользовательского интерфейса",
|
||||
"UnableToAddANewApplicationPleaseTryAgain": "Невозможно добавить новое уведомление, попробуйте еще раз.",
|
||||
"UnableToAddANewAppProfilePleaseTryAgain": "Не удалось добавить новый профиль качества. Повторите попытку.",
|
||||
"UnableToAddANewDownloadClientPleaseTryAgain": "Не удалось добавить новый клиент загрузки, попробуйте еще раз.",
|
||||
"UnableToAddANewIndexerPleaseTryAgain": "Не удалось добавить новый индексатор, повторите попытку.",
|
||||
"UnableToAddANewIndexerProxyPleaseTryAgain": "Не удалось добавить новый индексатор, повторите попытку.",
|
||||
"UnableToAddANewNotificationPleaseTryAgain": "Невозможно добавить новое уведомление, попробуйте еще раз.",
|
||||
"UnableToLoadBackups": "Невозможно загрузить резервные копии",
|
||||
"DownloadClientsLoadError": "Невозможно загрузить загрузчики",
|
||||
"UnableToLoadGeneralSettings": "Невозможно загрузить общие настройки",
|
||||
"UnableToLoadNotifications": "Невозможно загрузить уведомления",
|
||||
"UnableToLoadUISettings": "Не удалось загрузить настройки пользовательского интерфейса",
|
||||
"UnableToAddANewApplicationPleaseTryAgain": "Не удалось добавить новое приложение, попробуйте ещё раз.",
|
||||
"UnableToAddANewAppProfilePleaseTryAgain": "Не удалось добавить новый профиль приложения, попробуйте ещё раз.",
|
||||
"UnableToAddANewDownloadClientPleaseTryAgain": "Не удалось добавить новый клиент загрузки, попробуйте ещё раз.",
|
||||
"UnableToAddANewIndexerPleaseTryAgain": "Не удалось добавить новый индексатор, попробуйте ещё раз.",
|
||||
"UnableToAddANewIndexerProxyPleaseTryAgain": "Не удалось добавить новый прокси индексатора, попробуйте ещё раз.",
|
||||
"UnableToAddANewNotificationPleaseTryAgain": "Не удалось добавить новое уведомление, попробуйте ещё раз.",
|
||||
"UnableToLoadBackups": "Не удалось загрузить резервные копии",
|
||||
"DownloadClientsLoadError": "Не удалось загрузить клиенты загрузки",
|
||||
"UnableToLoadGeneralSettings": "Не удалось загрузить общие настройки",
|
||||
"UnableToLoadNotifications": "Не удалось загрузить уведомления",
|
||||
"UnableToLoadUISettings": "Не удалось загрузить настройки интерфейса",
|
||||
"UnsavedChanges": "Несохраненные изменения",
|
||||
"UnselectAll": "Снять все выделения",
|
||||
"UpdateAutomaticallyHelpText": "Автоматически загружать и устанавливать обновления. Вы так же можете установить в Система: Обновления",
|
||||
"UpdateStartupNotWritableHealthCheckMessage": "Невозможно установить обновление так как загрузочная папка '{startupFolder}' недоступна для записи для пользователя '{userName}'.",
|
||||
"UpdateStartupTranslocationHealthCheckMessage": "Не удается установить обновление, поскольку папка автозагрузки \"{startupFolder}\" находится в папке перемещения приложений.",
|
||||
"UpdateUiNotWritableHealthCheckMessage": "Невозможно установить обновление так как UI папка '{uiFolder}' недоступна для записи для пользователя '{userName}'.",
|
||||
"UpdateScriptPathHelpText": "Путь к пользовательскому скрипту, который обрабатывает остатки после процесса обновления",
|
||||
"UnselectAll": "Снять выделение со всех",
|
||||
"UpdateAutomaticallyHelpText": "Автоматически загружать и устанавливать обновления Вы по-прежнему сможете выполнить установку из раздела Система: Обновления",
|
||||
"UpdateStartupNotWritableHealthCheckMessage": "Невозможно установить обновление, так как каталог автозагрузки '{startupFolder}' недоступен для записи для пользователя '{userName}'.",
|
||||
"UpdateStartupTranslocationHealthCheckMessage": "Невозможно установить обновление, так как каталог автозагрузки '{startupFolder}' находится в каталоге перемещения приложений.",
|
||||
"UpdateUiNotWritableHealthCheckMessage": "Невозможно установить обновление, так как каталог интерфейса '{uiFolder}' недоступен для записи для пользователя '{userName}'.",
|
||||
"UpdateScriptPathHelpText": "Путь к пользовательскому скрипту, который извлекает пакет обновления и обрабатывает оставшуюся часть процесса обновления",
|
||||
"Uptime": "Время работы",
|
||||
"URLBase": "Базовый URL",
|
||||
"UrlBaseHelpText": "Для поддержки обратного прокси, по умолчанию пусто",
|
||||
"URLBase": "Базовый URL-адрес",
|
||||
"UrlBaseHelpText": "Для поддержки обратного прокси, значение по умолчанию - пустая строка",
|
||||
"UseProxy": "Использовать прокси",
|
||||
"Username": "Пользователь",
|
||||
"Username": "Имя пользователя",
|
||||
"Version": "Версия",
|
||||
"View": "Просмотр",
|
||||
"AddingTag": "Добавить тэг",
|
||||
"SSLPort": "SSL порт",
|
||||
"UILanguageHelpText": "Язык, который {appName} будет использовать для пользовательского интерфейса",
|
||||
"EnableSslHelpText": " Требуется перезапуск от администратора",
|
||||
"AddingTag": "Добавление тега",
|
||||
"SSLPort": "Порт SSL",
|
||||
"UILanguageHelpText": "Язык, используемый {appName} для интерфейса пользователя",
|
||||
"EnableSslHelpText": " Для применения изменений требуется перезапуск с правами администратора",
|
||||
"ErrorLoadingContents": "Ошибка при загрузке контента",
|
||||
"Events": "События",
|
||||
"EventType": "Тип события",
|
||||
"Exception": "Исключение",
|
||||
"FeatureRequests": "Будущие запросы",
|
||||
"FeatureRequests": "Запросы функций",
|
||||
"Warn": "Предупреждение",
|
||||
"Wiki": "Wiki",
|
||||
"YesCancel": "Да, отменить",
|
||||
"YesCancel": "Да, Отмена",
|
||||
"Yesterday": "Вчера",
|
||||
"NotificationTriggers": "Триггеры уведомления",
|
||||
"ApplicationStatusCheckAllClientMessage": "Все листы недоступны из-за ошибок",
|
||||
"ApplicationStatusCheckAllClientMessage": "Все приложения недоступны из-за ошибок",
|
||||
"Automatic": "Автоматически",
|
||||
"DeleteApplicationMessageText": "Вы уверены, что хотите удалить уведомление '{0}'?",
|
||||
"DeleteDownloadClient": "Удалить программу для скачивания",
|
||||
"DeleteApplicationMessageText": "Вы уверены, что хотите удалить приложение '{name}'?",
|
||||
"DeleteDownloadClient": "Удалить клиент загрузки",
|
||||
"EnableInteractiveSearchHelpText": "Будет использовано при интерактивном поиске",
|
||||
"Error": "Ошибка",
|
||||
"NoChange": "Нет изменений",
|
||||
"Age": "Возраст",
|
||||
"All": "Все",
|
||||
"AllIndexersHiddenDueToFilter": "Все фильмы спрятаны в соответствии с фильтром.",
|
||||
"AppDataDirectory": "Директория AppData",
|
||||
"AllIndexersHiddenDueToFilter": "Все индексаторы скрыты применённым фильтром.",
|
||||
"AppDataDirectory": "Каталог AppData",
|
||||
"Reddit": "Reddit",
|
||||
"System": "Система",
|
||||
"TableOptions": "Опции таблицы",
|
||||
"TagCannotBeDeletedWhileInUse": "Невозможно удалить во время использования",
|
||||
"TableOptions": "Параметры таблицы",
|
||||
"TagCannotBeDeletedWhileInUse": "Удаление невозможно во время использования",
|
||||
"Tags": "Теги",
|
||||
"ApplicationStatusCheckSingleClientMessage": "Листы недоступны из-за ошибок: {0}",
|
||||
"ApplicationStatusCheckSingleClientMessage": "Приложения недоступны из-за ошибок: {0}",
|
||||
"EditIndexer": "Редактировать индексатор",
|
||||
"MaintenanceRelease": "Техническая версия: исправлены ошибки и другие улучшения. Дополнительную информацию см. в истории коммитов Github",
|
||||
"MaintenanceRelease": "Технический релиз: исправление ошибок и другие улучшения. Подробнее см. в истории коммитов Github.",
|
||||
"Filters": "Фильтры",
|
||||
"HistoryCleanupDaysHelpText": "Установите 0, чтобы отключить автоматическую очистку",
|
||||
"HistoryCleanupDaysHelpTextWarning": "Файлы в корзине старше указанного количества дней будут очищены автоматически",
|
||||
"OnApplicationUpdateHelpText": "О обновлении приложения",
|
||||
"OnGrab": "При захвате",
|
||||
"OnHealthIssue": "О проблемах в системе",
|
||||
"HistoryCleanupDaysHelpTextWarning": "Элементы истории, старше указанного количества дней, будут автоматически удалены",
|
||||
"OnApplicationUpdateHelpText": "При обновлении приложения",
|
||||
"OnGrab": "При захвате релиза",
|
||||
"OnHealthIssue": "При проблемах с состоянием",
|
||||
"OnApplicationUpdate": "При обновлении приложения",
|
||||
"TestAllIndexers": "Тестировать все индексаторы",
|
||||
"UserAgentProvidedByTheAppThatCalledTheAPI": "User-Agent, представленный приложением, который вызывает API",
|
||||
"NotificationTriggersHelpText": "Выберите, какие события должны вызвать это уведомление",
|
||||
"UserAgentProvidedByTheAppThatCalledTheAPI": "User-Agent, предоставленный приложением, вызвавшим API",
|
||||
"NotificationTriggersHelpText": "Выберите события, которые должны вызвать это уведомление",
|
||||
"NetCore": ".NET",
|
||||
"GrabReleases": "Захватить релиз",
|
||||
"GrabReleases": "Захватить релиз(ы)",
|
||||
"UnableToLoadIndexers": "Не удалось загрузить индексаторы",
|
||||
"Link": "Ссылки",
|
||||
"MappedDrivesRunningAsService": "Подключённые сетевые диски недоступны, если программа запущена как сервис. Обратитесь в FAQ за дополнительной информацией",
|
||||
"Link": "Ссылка",
|
||||
"MappedDrivesRunningAsService": "Сопоставленные сетевые диски недоступны при запуске в качестве службы Windows. См. FAQ для получения дополнительной информации",
|
||||
"No": "Нет",
|
||||
"Yes": "Да",
|
||||
"AddRemoveOnly": "Добавить и Удалить Только",
|
||||
"AddNewIndexer": "Добавить Новый Индексатор",
|
||||
"AddToDownloadClient": "Добавить выпуск в Загрузочный клиент",
|
||||
"AddedToDownloadClient": "Выпуск добавлен в клиент",
|
||||
"AddDownloadClientToProwlarr": "Добавление клиента загрузки позволяет {appName} отправлять выпуски прямо из пользовательского интерфейса, выполняя поиск вручную.",
|
||||
"AddIndexerProxy": "Добавить индексатор прокси",
|
||||
"Connect": "Оповещения",
|
||||
"Notification": "Оповещения",
|
||||
"AddRemoveOnly": "Добавить и удалить только",
|
||||
"AddNewIndexer": "Добавить новый индексатор",
|
||||
"AddToDownloadClient": "Добавить релиз в клиент загрузки",
|
||||
"AddedToDownloadClient": "Релиз добавлен в клиент",
|
||||
"AddDownloadClientToProwlarr": "Добавление клиента загрузки позволяет {appName} отправлять релизы напрямую из пользовательского интерфейса, во время ручного поиска.",
|
||||
"AddIndexerProxy": "Добавить прокси для индексатора",
|
||||
"Connect": "Уведомления",
|
||||
"Notification": "Уведомление",
|
||||
"Encoding": "Кодирование",
|
||||
"Applications": "Приложения",
|
||||
"Application": "Приложения",
|
||||
"Notifications": "Оповещения",
|
||||
"Notifications": "Уведомления",
|
||||
"InstanceName": "Имя экземпляра",
|
||||
"InstanceNameHelpText": "Имя экземпляра на вкладке и имя приложения системного журнала",
|
||||
"Started": "Запущено",
|
||||
"Database": "База данных",
|
||||
"Duration": "Длительность",
|
||||
"ApplicationLongTermStatusCheckSingleClientMessage": "Все индексаторы недоступны из-за ошибок за последние 6 часов: {0}",
|
||||
"Ended": "Завершен",
|
||||
"ApplicationLongTermStatusCheckSingleClientMessage": "Все приложения недоступны из-за ошибок более 6 часов: {0}",
|
||||
"Ended": "Завершён",
|
||||
"LastExecution": "Последнее выполнение",
|
||||
"NextExecution": "Следующее выполнение",
|
||||
"NextExecution": "Следующий запуск",
|
||||
"Queued": "В очереди",
|
||||
"ApplicationLongTermStatusCheckAllClientMessage": "Все индексаторы недоступны из-за ошибок за последние 6 часов",
|
||||
"ApplicationLongTermStatusCheckAllClientMessage": "Все приложения недоступны из-за ошибок более 6 часов",
|
||||
"LastDuration": "Последняя длительность",
|
||||
"ThemeHelpText": "Измените тему пользовательского интерфейса приложения, тема «Авто» будет использовать тему вашей ОС для установки светлого или темного режима. Вдохновленный Theme.Park",
|
||||
"ThemeHelpText": "Изменить тему интерфейса приложения. Тема 'Авто' будет использовать тему вашей ОС для выбора светлого или тёмного режима. Вдохновлено {inspiredBy}.",
|
||||
"Remove": "Удалить",
|
||||
"Replace": "Заменить",
|
||||
"TheLatestVersionIsAlreadyInstalled": "Последняя версия {appName} уже установлена",
|
||||
@@ -356,180 +356,422 @@
|
||||
"ApplicationUrlHelpText": "Внешний URL-адрес этого приложения, включая http(s)://, порт и базовый URL-адрес",
|
||||
"Label": "Метка",
|
||||
"ApplyChanges": "Применить изменения",
|
||||
"ApplyTagsHelpTextRemove": "Удалить: удалить введенные теги",
|
||||
"DeleteSelectedApplicationsMessageText": "Вы уверены что хотите удалить индексер '{0}'?",
|
||||
"ApplyTagsHelpTextRemove": "Удалить: Удалить введённые теги",
|
||||
"DeleteSelectedApplicationsMessageText": "Вы уверены, что хотите удалить выбранные приложения: {count}?",
|
||||
"DeleteSelectedDownloadClients": "Удалить клиент(ы) загрузки",
|
||||
"DeleteSelectedDownloadClientsMessageText": "Вы уверены, что хотите удалить {count} выбранных клиента загрузки?",
|
||||
"DeleteSelectedIndexersMessageText": "Вы уверены, что хотите удалить {count} выбранных индексатора?",
|
||||
"DownloadClientPriorityHelpText": "Установите приоритет нескольких клиентов загрузки. Круговой алгоритм используется для клиентов с таким же приоритетом.",
|
||||
"DeleteSelectedDownloadClientsMessageText": "Вы уверены, что хотите удалить выбранные клиенты загрузки: {count}?",
|
||||
"DeleteSelectedIndexersMessageText": "Вы уверены, что хотите удалить выбранные индексаторы: {count}?",
|
||||
"DownloadClientPriorityHelpText": "Установить приоритет для нескольких клиентов загрузки. Клиенты с одинаковым приоритетом обрабатываются по круговой системе.",
|
||||
"EditSelectedDownloadClients": "Редактировать выбранные клиенты загрузки",
|
||||
"ApiKeyValidationHealthCheckMessage": "Пожалуйста, обновите свой ключ API, чтобы он был длиной не менее {length} символов. Вы можете сделать это через настройки или файл конфигурации",
|
||||
"Genre": "Жанры",
|
||||
"ApiKeyValidationHealthCheckMessage": "Пожалуйста, обновите свой ключ API, чтобы он был длиной не менее {length} символов в длину. Вы можете сделать это через настройки или файл конфигурации",
|
||||
"Genre": "Жанр",
|
||||
"Theme": "Тема",
|
||||
"Year": "Год",
|
||||
"ApplyTagsHelpTextAdd": "Добавить: Добавьте теги в существующий список тегов",
|
||||
"ApplyTagsHelpTextHowToApplyApplications": "Как добавить ярлыки к выбранным фильмам",
|
||||
"ApplyTagsHelpTextAdd": "Добавить: Добавить теги к существующему списку тегов",
|
||||
"ApplyTagsHelpTextHowToApplyApplications": "Как применить теги к выбранным приложениям",
|
||||
"ApplyTagsHelpTextHowToApplyIndexers": "Как применить теги к выбранным индексаторам",
|
||||
"ApplyTagsHelpTextReplace": "Заменить: заменить теги введенными тегами (оставьте поле пустым, чтобы удалить все теги)",
|
||||
"Track": "След",
|
||||
"UpdateAvailableHealthCheckMessage": "Доступно новое обновление",
|
||||
"More": "Более",
|
||||
"ApplyTagsHelpTextReplace": "Заменить: Заменить теги введёнными тегами (оставьте поле пустым, чтобы удалить все теги)",
|
||||
"Track": "Трек",
|
||||
"UpdateAvailableHealthCheckMessage": "Доступно новое обновление: {version}",
|
||||
"More": "Ещё",
|
||||
"Publisher": "Издатель",
|
||||
"ConnectionLostReconnect": "{appName} попытается соединиться автоматически или нажмите кнопку внизу.",
|
||||
"ConnectionLostReconnect": "{appName} попытается соединиться автоматически или нажмите кнопку ниже.",
|
||||
"ConnectionLostToBackend": "{appName} потерял связь с сервером и его необходимо перезагрузить, чтобы восстановить работоспособность.",
|
||||
"RecentChanges": "Последние изменения",
|
||||
"WhatsNew": "Что нового?",
|
||||
"minutes": "Минуты",
|
||||
"DeleteAppProfileMessageText": "Вы действительно хотите удалить профиль качества {0}",
|
||||
"minutes": "минуты",
|
||||
"DeleteAppProfileMessageText": "Вы уверены, что хотите удалить профиль приложения '{name}'?",
|
||||
"EditDownloadClientImplementation": "Редактировать клиент загрузки - {implementationName}",
|
||||
"EditIndexerImplementation": "Редактировать индексатор - {implementationName}",
|
||||
"NoIndexersFound": "Индексаторы не найдены",
|
||||
"AuthenticationRequiredHelpText": "Отредактируйте, для каких запросов требуется аутентификация. Не меняйте, пока не поймете все риски.",
|
||||
"AuthenticationRequiredHelpText": "Отредактируйте, для каких запросов требуется авторизация. Не изменяйте, если не понимаете риски.",
|
||||
"AuthenticationRequired": "Требуется авторизация",
|
||||
"CountDownloadClientsSelected": "{count} выбранных клиентов загрузки",
|
||||
"CountIndexersSelected": "{count} выбранных индексаторов",
|
||||
"CountDownloadClientsSelected": "Выбрано клиентов загрузки: {count}",
|
||||
"CountIndexersSelected": "Выбрано индексаторов: {count}",
|
||||
"EditSelectedIndexers": "Редактировать выбранный индексатор",
|
||||
"OnHealthRestored": "При восстановлении работоспособности",
|
||||
"OnHealthRestoredHelpText": "При восстановлении работоспособности",
|
||||
"OnHealthRestored": "При восстановлении состояния",
|
||||
"OnHealthRestoredHelpText": "При восстановлении состояния",
|
||||
"Implementation": "Реализация",
|
||||
"NoDownloadClientsFound": "Клиенты для загрузки не найдены",
|
||||
"NoDownloadClientsFound": "Клиенты загрузки не найдены",
|
||||
"IndexerDownloadClientHealthCheckMessage": "Индексаторы с недопустимыми клиентами загрузки: {indexerNames}.",
|
||||
"StopSelecting": "Прекратить выбор",
|
||||
"StopSelecting": "Отменить выбор",
|
||||
"AddDownloadClientImplementation": "Добавить клиент загрузки - {implementationName}",
|
||||
"AddConnection": "Добавить подключение",
|
||||
"AddConnectionImplementation": "Добавить подключение - {implementationName}",
|
||||
"ManageDownloadClients": "Менеджер клиентов загрузки",
|
||||
"NotificationStatusAllClientHealthCheckMessage": "Все уведомления недоступны из-за сбоев",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Уведомления недоступны из-за сбоев: {notificationNames}",
|
||||
"ManageDownloadClients": "Управление клиентами загрузки",
|
||||
"NotificationStatusAllClientHealthCheckMessage": "Все уведомления недоступны из-за ошибок",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Уведомления недоступны из-за ошибок: {notificationNames}",
|
||||
"AddIndexerImplementation": "Добавить индексатор - {implementationName}",
|
||||
"AddIndexerProxyImplementation": "Добавить индексатор - {implementationName}",
|
||||
"EditConnectionImplementation": "Добавить соединение - {implementationName}",
|
||||
"EditIndexerProxyImplementation": "Редактировать индексатор - {implementationName}",
|
||||
"AddIndexerProxyImplementation": "Добавить прокси для индексатора - {implementationName}",
|
||||
"EditConnectionImplementation": "Редактировать соединение - {implementationName}",
|
||||
"EditIndexerProxyImplementation": "Редактировать прокси индексатора - {implementationName}",
|
||||
"Season": "Сезон",
|
||||
"AddApplicationImplementation": "Добавить соединение - {implementationName}",
|
||||
"EditApplicationImplementation": "Редактировать уведомление - {implementationName}",
|
||||
"AuthBasic": "Базовый (всплывающее окно браузера)",
|
||||
"AddApplicationImplementation": "Добавить приложение - {implementationName}",
|
||||
"EditApplicationImplementation": "Редактировать приложение - {implementationName}",
|
||||
"AuthBasic": "Базовый (Всплывающее окно браузера)",
|
||||
"AuthForm": "Формы (Страница авторизации)",
|
||||
"DisabledForLocalAddresses": "Отключено для локальных адресов",
|
||||
"None": "Ничто",
|
||||
"ResetAPIKeyMessageText": "Вы уверены, что хотите сбросить Ваш API ключ?",
|
||||
"None": "Ничего",
|
||||
"ResetAPIKeyMessageText": "Вы уверены, что хотите сбросить ключ API?",
|
||||
"Categories": "Категории",
|
||||
"Album": "альбом",
|
||||
"Album": "Альбом",
|
||||
"AddCustomFilter": "Добавить специальный фильтр",
|
||||
"AuthenticationMethod": "Способ авторизации",
|
||||
"AuthenticationRequiredPasswordHelpTextWarning": "Введите новый пароль",
|
||||
"AuthenticationRequiredUsernameHelpTextWarning": "Введите новое имя пользователя",
|
||||
"RestartProwlarr": "Перезапустить {appName}",
|
||||
"AuthenticationRequiredWarning": "Чтобы предотвратить удаленный доступ без авторизации, {appName} теперь требует, чтобы авторизация была включена. При желании вы можете отключить авторизацию с локальных адресов.",
|
||||
"AuthenticationRequiredWarning": "Чтобы предотвратить удалённый доступ без авторизации, {appName} теперь требует включения авторизации. Вы можете опционально отключить авторизацию для локальных адресов.",
|
||||
"Id": "ID",
|
||||
"ManageClients": "Управление клиентами",
|
||||
"CountApplicationsSelected": "{count} коллекция(ий) выбрано",
|
||||
"CountApplicationsSelected": "Выбрано приложений: {count}",
|
||||
"IndexerHDBitsSettingsCodecs": "Кодеки",
|
||||
"IndexerHDBitsSettingsMediums": "Средний",
|
||||
"IndexerHDBitsSettingsMediums": "Mediums",
|
||||
"Directory": "Каталог",
|
||||
"CustomFilter": "Настраиваемые фильтры",
|
||||
"CustomFilter": "Настраиваемый фильтр",
|
||||
"GrabRelease": "Захватить релиз",
|
||||
"ManualGrab": "Ручной захват",
|
||||
"OverrideAndAddToDownloadClient": "Замена и добавление в очередь загрузки",
|
||||
"OverrideAndAddToDownloadClient": "Переопределить и добавить в клиент загрузки",
|
||||
"OverrideGrabModalTitle": "Переопределить и захватить - {title}",
|
||||
"PrioritySettings": "Приоритет: {priority}",
|
||||
"SelectDownloadClientModalTitle": "{modalTitle} - Выберите клиент для загрузки",
|
||||
"ProxyValidationBadRequest": "Не удалось проверить прокси. Код: {statusCode}",
|
||||
"SelectDownloadClientModalTitle": "{modalTitle} - Выберите клиент загрузки",
|
||||
"ProxyValidationBadRequest": "Не удалось протестировать прокси. Код состояния: {statusCode}",
|
||||
"Default": "По умолчанию",
|
||||
"AppUpdated": "{appName} обновлен",
|
||||
"AppUpdatedVersion": "Приложение {appName} обновлено до версии `{version}`. Чтобы получить последние изменения, вам необходимо перезагрузить приложение {appName}",
|
||||
"AppUpdated": "{appName} обновлён",
|
||||
"AppUpdatedVersion": "{appName} обновлён до версии `{version}`, для получения последних изменений необходимо перезагрузить {appName}",
|
||||
"Episode": "Эпизод",
|
||||
"Donate": "Пожертвовать",
|
||||
"DownloadClientFreeboxSettingsAppTokenHelpText": "Токен приложения, полученный при создании доступа к API Freebox (т. е. 'app_token')",
|
||||
"DownloadClientPneumaticSettingsNzbFolder": "Nzb папка",
|
||||
"DownloadClientPneumaticSettingsStrmFolder": "Strm папка",
|
||||
"DownloadClientFreeboxSettingsAppTokenHelpText": "Токен приложения, полученный при создании доступа к Freebox API (например, 'app_token')",
|
||||
"DownloadClientPneumaticSettingsNzbFolder": "Каталог NZB",
|
||||
"DownloadClientPneumaticSettingsStrmFolder": "Каталог STRM",
|
||||
"DownloadClientQbittorrentSettingsContentLayout": "Макет контента",
|
||||
"DownloadClientQbittorrentSettingsSequentialOrderHelpText": "Скачать в последовательном порядке (qBittorrent 4.1.0+)",
|
||||
"DownloadClientQbittorrentSettingsSequentialOrderHelpText": "Загружать последовательно (qBittorrent 4.1.0+)",
|
||||
"DownloadClientRTorrentSettingsUrlPath": "URL-путь",
|
||||
"DownloadClientRTorrentSettingsDirectoryHelpText": "Опциональное место для загрузок. Оставьте пустым, чтобы использовать каталог rTorrent по умолчанию",
|
||||
"DownloadClientSettingsAddPaused": "Добавить приостановленное",
|
||||
"DownloadClientRTorrentSettingsDirectoryHelpText": "Необязательное место для сохранения загрузок, оставьте поле пустым, чтобы использовать расположение rTorrent по умолчанию",
|
||||
"DownloadClientSettingsAddPaused": "Добавить приостановленные",
|
||||
"DownloadClientSettingsInitialState": "Начальное состояние",
|
||||
"DownloadClientTransmissionSettingsUrlBaseHelpText": "Добавляет префикс к URL-адресу RPC {clientName}, например {url}, по умолчанию — '{defaultUrl}'",
|
||||
"NotificationsTelegramSettingsIncludeAppName": "Включить {appName} в заголовок",
|
||||
"Category": "Категория",
|
||||
"Clone": "Клонировать",
|
||||
"DefaultNameCopiedProfile": "{name} - Копировать",
|
||||
"DownloadClientFreeboxSettingsHostHelpText": "Имя хоста или IP-адрес хоста Freebox, по умолчанию — '{url}' (будет работать только в той же сети)",
|
||||
"DownloadClientFreeboxSettingsHostHelpText": "Имя хоста или IP-адрес хоста Freebox, по умолчанию — '{url}' (будет работать только если находится в одной сети)",
|
||||
"DownloadClientFreeboxSettingsPortHelpText": "Порт, используемый для доступа к интерфейсу Freebox, по умолчанию — '{port}'",
|
||||
"DownloadClientPneumaticSettingsNzbFolderHelpText": "Эта папка должна быть доступна из XBMC",
|
||||
"DownloadClientQbittorrentSettingsSequentialOrder": "Последовательный порядок",
|
||||
"DownloadClientSettingsDestinationHelpText": "Вручную указывает место назначения загрузки. Оставьте поле пустым, чтобы использовать значение по умолчанию",
|
||||
"DownloadClientSettingsInitialStateHelpText": "Исходное состояние торрентов, добавленных в {clientName}",
|
||||
"DownloadClientPneumaticSettingsNzbFolderHelpText": "Этот каталог должен быть доступен из XBMC",
|
||||
"DownloadClientQbittorrentSettingsSequentialOrder": "Загружать последовательно",
|
||||
"DownloadClientSettingsDestinationHelpText": "Ручная установка места загрузки, оставьте пустым для использования значения по умолчанию",
|
||||
"DownloadClientSettingsInitialStateHelpText": "Начальное состояние для торрентов, добавленных в {clientName}",
|
||||
"DownloadClientRTorrentSettingsAddStopped": "Добавить остановленные",
|
||||
"External": "Внешний",
|
||||
"Destination": "Место назначения",
|
||||
"BlackholeFolderHelpText": "Папка, в которой {appName} будет хранить файл {extension}",
|
||||
"DownloadClientDownloadStationSettingsDirectoryHelpText": "Опциональная общая папка для размещения загрузок. Оставьте пустым, чтобы использовать каталог Download Station по умолчанию",
|
||||
"BlackholeFolderHelpText": "Каталог, в котором {appName} будет хранить файл {extension}",
|
||||
"DownloadClientDownloadStationSettingsDirectoryHelpText": "Необязательный общий каталог для сохранения загрузок, оставьте поле пустым, чтобы использовать расположение Download Station по умолчанию",
|
||||
"DownloadClientFloodSettingsAdditionalTags": "Дополнительные теги",
|
||||
"DownloadClientFloodSettingsUrlBaseHelpText": "Добавляет префикс к Flood API, например {url}",
|
||||
"DownloadClientFloodSettingsUrlBaseHelpText": "Добавляет префикс к Flood API, такой как {url}",
|
||||
"DownloadClientFreeboxSettingsApiUrl": "URL-адрес API",
|
||||
"DownloadClientFreeboxSettingsApiUrlHelpText": "Определите базовый URL-адрес Freebox API с версией API, например '{url}', по умолчанию — '{defaultApiUrl}'",
|
||||
"DownloadClientFreeboxSettingsAppId": "Идентификатор приложения",
|
||||
"DownloadClientFreeboxSettingsAppIdHelpText": "Идентификатор приложения, указанный при создании доступа к Freebox API (т. е. 'app_id')",
|
||||
"DownloadClientFreeboxSettingsAppId": "ID приложения",
|
||||
"DownloadClientFreeboxSettingsAppIdHelpText": "ID приложения, полученный при создании доступа к Freebox API (например, 'app_id')",
|
||||
"DownloadClientFreeboxSettingsAppToken": "Токен приложения",
|
||||
"DownloadClientFloodSettingsAdditionalTagsHelpText": "Добавляет свойства мультимедиа в виде тегов. Подсказки являются примерами.",
|
||||
"DownloadClientNzbgetSettingsAddPausedHelpText": "Для этой опции требуется как минимум NzbGet версии 16.0",
|
||||
"DownloadClientPneumaticSettingsStrmFolderHelpText": "Файлы .strm в этой папке будут импортированы дроном",
|
||||
"DownloadClientQbittorrentSettingsFirstAndLastFirst": "Первый и последний Первый",
|
||||
"DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText": "Сначала скачайте первую и последнюю части (qBittorrent 4.1.0+)",
|
||||
"DownloadClientNzbgetSettingsAddPausedHelpText": "Для работы этого параметра требуется версия NzbGet не ниже 16.0",
|
||||
"DownloadClientPneumaticSettingsStrmFolderHelpText": "Файлы .strm в этом каталоге будут импортированы дроном",
|
||||
"DownloadClientQbittorrentSettingsFirstAndLastFirst": "Первое и последнее сначала",
|
||||
"DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText": "Загружать первые и последние части сначала (qBittorrent 4.1.0+)",
|
||||
"UseSsl": "Использовать SSL",
|
||||
"AuthenticationMethodHelpTextWarning": "Пожалуйста, выберите действительный метод аутентификации",
|
||||
"AuthenticationMethodHelpTextWarning": "Пожалуйста, выберите допустимый метод авторизации",
|
||||
"XmlRpcPath": "Путь XML RPC",
|
||||
"UsenetBlackholeNzbFolder": "Nzb папка",
|
||||
"UsenetBlackholeNzbFolder": "Каталог NZB",
|
||||
"AuthenticationRequiredPasswordConfirmationHelpTextWarning": "Подтвердите новый пароль",
|
||||
"DownloadClientAriaSettingsDirectoryHelpText": "Опциональное местоположение для загрузок. Оставьте пустым, чтобы использовать местоположение Aria2 по умолчанию",
|
||||
"DownloadClientSettingsUrlBaseHelpText": "Добавляет префикс к URL-адресу {clientName}, например {url}",
|
||||
"DownloadClientAriaSettingsDirectoryHelpText": "Необязательное место для сохранения загрузок, оставьте поле пустым, чтобы использовать расположение Aria2 по умолчанию",
|
||||
"DownloadClientSettingsUrlBaseHelpText": "Добавляет префикс к URL-адресу {clientName}, например, {url}",
|
||||
"NoHistoryFound": "История не найдена",
|
||||
"DownloadClientFloodSettingsTagsHelpText": "Начальные теги загрузки. Чтобы быть распознанным, загрузка должна иметь все начальные теги. Это позволяет избежать конфликтов с несвязанными загрузками.",
|
||||
"DownloadClientQbittorrentSettingsContentLayoutHelpText": "Использовать ли настроенный макет контента qBittorrent, исходный макет из торрента или всегда создавать подпапку (qBittorrent 4.3.2+)",
|
||||
"DownloadClientQbittorrentSettingsInitialStateHelpText": "Исходное состояние торрентов, добавленных в qBittorrent. Обратите внимание, что принудительные торренты не подчиняются ограничениям на раздачу",
|
||||
"DownloadClientDelugeSettingsUrlBaseHelpText": "Добавляет префикс к URL-адресу json deluge, см. {url}",
|
||||
"NotificationsTelegramSettingsIncludeAppNameHelpText": "При необходимости добавьте к заголовку сообщения префикс {appName}, чтобы отличать уведомления от разных приложений",
|
||||
"DownloadClientQbittorrentSettingsUseSslHelpText": "Используйте безопасное соединение. См. «Параметры» -> «Веб-интерфейс» -> «Использовать HTTPS вместо HTTP» в qBittorrent.",
|
||||
"DownloadClientRTorrentSettingsAddStoppedHelpText": "Включение добавит торренты и магниты в rTorrent в остановленном состоянии. Это может привести к поломке магнет файлов.",
|
||||
"DownloadClientRTorrentSettingsUrlPathHelpText": "Путь к конечной точке XMLRPC см. в {url}. Обычно это RPC2 или [путь к ruTorrent]{url2} при использовании ruTorrent.",
|
||||
"DownloadClientSettingsUseSslHelpText": "Использовать безопасное соединение при подключении к {clientName}",
|
||||
"DownloadClientTransmissionSettingsDirectoryHelpText": "Опциональное место для загрузок. Оставьте пустым, чтобы использовать каталог Transmission по умолчанию",
|
||||
"DownloadClientFloodSettingsTagsHelpText": "Начальные теги загрузки. Чтобы быть распознанным, загрузка должна иметь все начальные теги. Это предотвращает конфликты с несвязанными загрузками.",
|
||||
"DownloadClientQbittorrentSettingsContentLayoutHelpText": "Выбрать расположение контента: настроенное в qBittorrent, исходный макет из торрента или всегда создавать подкаталог (qBittorrent 4.3.2+)",
|
||||
"DownloadClientQbittorrentSettingsInitialStateHelpText": "Исходное состояние торрентов, добавленных в qBittorrent. Обратите внимание, что принудительные торренты не соблюдают ограничения на раздачу",
|
||||
"DownloadClientDelugeSettingsUrlBaseHelpText": "Добавляет префикс к URL-адресу json Deluge, см.: {url}",
|
||||
"NotificationsTelegramSettingsIncludeAppNameHelpText": "При необходимости добавить к заголовку сообщения префикс {appName}, чтобы различать уведомления от разных приложений",
|
||||
"DownloadClientQbittorrentSettingsUseSslHelpText": "Использовать защищённое соединение. Смотрите 'Параметры' -> 'Веб-интерфейс' -> 'Использовать HTTPS вместо HTTP' в qBittorrent.",
|
||||
"DownloadClientRTorrentSettingsAddStoppedHelpText": "Включение добавит торренты и магнет-ссылки в rTorrent в остановленном состоянии. Это может привести к повреждению магнет-файлов.",
|
||||
"DownloadClientRTorrentSettingsUrlPathHelpText": "Путь к конечной точке XMLRPC см. {url}. Обычно это RPC2 или [путь к ruTorrent]{url2} при использовании ruTorrent.",
|
||||
"DownloadClientSettingsUseSslHelpText": "Использовать защищённое соединение при подключении к {clientName}",
|
||||
"DownloadClientTransmissionSettingsDirectoryHelpText": "Необязательное место для сохранения загрузок, оставьте поле пустым, чтобы использовать расположение Transmission по умолчанию",
|
||||
"IndexerSettingsAdditionalParameters": "Дополнительные параметры",
|
||||
"IndexerSettingsSeedRatio": "Рейтинг",
|
||||
"IndexerSettingsSeedTimeHelpText": "Время, в течение которого торрент должен оставаться на раздаче перед остановкой, пусто: используется значение по умолчанию клиента загрузки",
|
||||
"IndexerSettingsSeedRatio": "Коэффициент раздачи",
|
||||
"IndexerSettingsSeedTimeHelpText": "Время, в течение которого торрент должен оставаться на раздаче перед остановкой, пусто — используется значение клиента загрузки по умолчанию",
|
||||
"PasswordConfirmation": "Подтверждение пароля",
|
||||
"TorrentBlackholeTorrentFolder": "Папка торрента",
|
||||
"TorrentBlackholeSaveMagnetFilesHelpText": "Сохраните магнитную ссылку, если файл .torrent недоступен (полезно только в том случае, если клиент загрузки поддерживает магниты, сохраненные в файле)",
|
||||
"TorrentBlackholeTorrentFolder": "Каталог торрента",
|
||||
"TorrentBlackholeSaveMagnetFilesHelpText": "Сохранить магнет-ссылку, если файл .torrent недоступен (полезно только в случае, если клиент загрузки поддерживает магнет-ссылки, сохранённые в файле)",
|
||||
"NotificationsEmailSettingsUseEncryption": "Использовать шифрование",
|
||||
"NotificationsEmailSettingsUseEncryptionHelpText": "Предпочитать использовать шифрование, если оно настроено на сервере, всегда использовать шифрование через SSL (только порт 465) или StartTLS (любой другой порт) или никогда не использовать шифрование",
|
||||
"NotificationsEmailSettingsUseEncryptionHelpText": "Выбрать режим шифрования: предпочитать шифрование, если оно настроено на сервере; всегда использовать шифрование через SSL (только порт 465) или StartTLS (любой другой порт); никогда не использовать шифрование.",
|
||||
"LabelIsRequired": "Требуется метка",
|
||||
"IndexerSettingsSeedTime": "Время сидирования",
|
||||
"Mixed": "Смешанный",
|
||||
"IndexerSettingsApiPath": "Путь API",
|
||||
"IndexerSettingsSeedRatioHelpText": "Рейтинг, которого должен достичь торрент перед остановкой, пустой использует значение по умолчанию клиента загрузки. Рейтинг должен быть не менее 1,0 и соответствовать правилам индексаторов",
|
||||
"IndexerSettingsSeedRatioHelpText": "Рейтинг, которого должен достичь торрент перед остановкой, пусто — используется значение по умолчанию клиента загрузки. Рейтинг должен быть не менее 1,0 и соответствовать правилам индексаторов",
|
||||
"InvalidUILanguage": "В вашем пользовательском интерфейсе установлен недопустимый язык. Исправьте его и сохраните настройки",
|
||||
"IndexerHDBitsSettingsCodecsHelpText": "Если не указано, используются все параметры.",
|
||||
"IndexerHDBitsSettingsMediumsHelpText": "Если не указано, используются все параметры.",
|
||||
"IndexerSettingsApiPathHelpText": "Путь к API, обычно {url}",
|
||||
"TorrentBlackholeSaveMagnetFilesExtension": "Сохранить расширение магнет файлов",
|
||||
"TorrentBlackholeSaveMagnetFiles": "Сохранить магнет файлы",
|
||||
"TorrentBlackholeSaveMagnetFilesExtension": "Сохранить магнет-файлы с расширением",
|
||||
"TorrentBlackholeSaveMagnetFiles": "Сохранить магнет-файлы",
|
||||
"IndexerSettingsCookie": "Cookie",
|
||||
"SecretToken": "Секретный токен",
|
||||
"TorrentBlackholeSaveMagnetFilesExtensionHelpText": "Расширение для магнитных ссылок, по умолчанию «.magnet»",
|
||||
"MinimumSeeders": "Минимум сидеров (раздающих)",
|
||||
"TorrentBlackholeSaveMagnetFilesExtensionHelpText": "Расширение для магнет-ссылок, по умолчанию '.magnet'",
|
||||
"MinimumSeeders": "Минимум сидеров",
|
||||
"SeedTime": "Время сидирования",
|
||||
"SeedRatio": "Рейтинг",
|
||||
"SeedRatio": "Коэффициент раздачи",
|
||||
"days": "дни",
|
||||
"ApplicationSettingsSyncRejectBlocklistedTorrentHashes": "Отклонять хэши торрентов из черного списка при захвате",
|
||||
"ApplicationSettingsSyncRejectBlocklistedTorrentHashes": "Отклонять хэши торрентов из чёрного списка при захвате",
|
||||
"Author": "Автор",
|
||||
"IndexerHDBitsSettingsOriginsHelpText": "Если не указано, используются все параметры.",
|
||||
"Any": "Любой",
|
||||
"ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "Если торрент заблокирован хешем, он может не быть должным образом отклонен во время RSS/поиска для некоторых индексаторов. Включение этого параметра позволит отклонить его после захвата торрента, но до его отправки клиенту.",
|
||||
"BuiltIn": "Встроено",
|
||||
"ProxyValidationUnableToConnect": "Невозможно подключиться к индексатору: {exceptionMessage}. Подробности см. в журнале этой ошибки",
|
||||
"ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "Если торрент заблокирован по хэшу, он может не правильно быть отклонён во время RSS/Поиска для некоторых индексаторов.",
|
||||
"BuiltIn": "Встроенный",
|
||||
"ProxyValidationUnableToConnect": "Не удалось подключиться к прокси: {exceptionMessage}. Проверьте журнал ошибок для получения дополнительной информации",
|
||||
"Script": "Скрипт",
|
||||
"InfoUrl": "URL-адрес информации",
|
||||
"PublishedDate": "Дата публикации",
|
||||
"AllSearchResultsHiddenByFilter": "Все результаты скрыты фильтром"
|
||||
"AllSearchResultsHiddenByFilter": "Все результаты поиска скрыты применённым фильтром.",
|
||||
"HealthMessagesInfoBox": "Дополнительную информацию о причине появления этих сообщений о проверке работоспособности можно найти, перейдя по ссылке wiki (значок книги) в конце строки или проверить [журналы]({link}). Если у вас возникли трудности с пониманием этих сообщений, вы можете обратиться в нашу службу поддержки по ссылкам ниже.",
|
||||
"PackageVersionInfo": "{packageVersion} создан {packageAuthor}",
|
||||
"TotalUserAgentQueries": "Всего запросов User Agent",
|
||||
"TotalIndexerSuccessfulGrabs": "Всего успешных захватов индексатора",
|
||||
"TotalIndexerQueries": "Всего запросов к индексатору",
|
||||
"TVSearchTypes": "Типы поиска ТВ-программ",
|
||||
"SyncLevelAddRemove": "Только добавление и удаление: при добавлении или удалении индексаторов из {appName} это удалённое приложение будет обновлено.",
|
||||
"SyncProfiles": "Профили синхронизации",
|
||||
"TorznabUrl": "URL-адрес Torznab",
|
||||
"TotalGrabs": "Всего захватов",
|
||||
"TotalQueries": "Всего запросов",
|
||||
"TotalHostGrabs": "Всего захватов с хоста",
|
||||
"TestAllApps": "Тестировать все приложения",
|
||||
"TotalUserAgentGrabs": "Всего захватов User Agent",
|
||||
"SyncProfile": "Профиль синхронизации",
|
||||
"TotalHostQueries": "Всего запросов к хосту",
|
||||
"SyncLevelFull": "Полная синхронизация: приложение будет поддерживать индексаторы полностью синхронизированными. Любые изменения, внесённые в индексаторы {appName}, будут синхронизированы с этим приложением.",
|
||||
"Menu": "Меню",
|
||||
"Artist": "Исполнитель",
|
||||
"OnGrabHelpText": "При захвате релиза",
|
||||
"Book": "Книга",
|
||||
"LogSizeLimit": "Ограничение размера журнала",
|
||||
"LogSizeLimitHelpText": "Максимальный размер файла журнала в МБ перед архивированием. По умолчанию - 1 МБ.",
|
||||
"AudioSearch": "Поиск аудио",
|
||||
"MusicSearchTypes": "Типы поиска музыки",
|
||||
"AreYouSureYouWantToDeleteCategory": "Вы уверены, что хотите удалить сопоставленную категорию?",
|
||||
"ClearHistory": "Очистить историю",
|
||||
"Privacy": "Конфиденциальность",
|
||||
"EnableIndexer": "Включить индексатор",
|
||||
"EnableRssHelpText": "Включить RSS-канал для индексатора",
|
||||
"Stats": "Статистика",
|
||||
"HistoryDetails": "Подробности истории",
|
||||
"IndexerDisabled": "Индексатор выключен",
|
||||
"IndexerFailureRate": "Процент неудач индексатора",
|
||||
"LastFailure": "Последняя ошибка",
|
||||
"MassEditor": "Пакетный редактор",
|
||||
"FullSync": "Полная синхронизация",
|
||||
"IndexerAuth": "Авторизация индексатора",
|
||||
"AddApplication": "Добавить приложение",
|
||||
"AddSyncProfile": "Добавить профиль синхронизации",
|
||||
"MappedCategories": "Сопоставленные категории",
|
||||
"Redirect": "Перенаправление",
|
||||
"ElapsedTime": "Прошедшее время",
|
||||
"FilterPlaceHolder": "Поисковые индексаторы",
|
||||
"Private": "Конфиденциальный",
|
||||
"AdvancedSettingsHiddenClickToShow": "Расширенные настройки скрыты, нажмите, чтобы показать",
|
||||
"BasicSearch": "Базовый поиск",
|
||||
"IndexerStatus": "Статус индексатора",
|
||||
"ManageApplications": "Управление приложениями",
|
||||
"PackSeedTime": "Время раздачи пакета",
|
||||
"SearchAllIndexers": "Поиск во всех индексаторах",
|
||||
"SettingsSqlLoggingHelpText": "Журналировать все SQL-запросы из {appName}",
|
||||
"DeleteAppProfile": "Удалить профиль приложения",
|
||||
"EditSyncProfile": "Редактировать профиль синхронизации",
|
||||
"EnabledRedirected": "Включено, Перенаправлено",
|
||||
"IndexerProxies": "Прокси индексатора",
|
||||
"InitialFailure": "Начальный сбой",
|
||||
"GoToApplication": "Перейти к приложению",
|
||||
"IndexerName": "Название индексатора",
|
||||
"NoSearchResultsFound": "Не обнаружены результаты поиска, повторите поиск ниже.",
|
||||
"QueryOptions": "Параметры запроса",
|
||||
"SearchTypes": "Типы поиска",
|
||||
"DeleteIndexerProxy": "Удалить прокси индексатора",
|
||||
"DeleteSelectedApplications": "Удалить выбранные приложения",
|
||||
"DevelopmentSettings": "Настройки разработки",
|
||||
"HistoryCleanup": "Очистка истории",
|
||||
"SearchCountIndexers": "Поиск в {count} индексаторе(ах)",
|
||||
"SelectIndexers": "Выберите индексаторы",
|
||||
"IndexerBeyondHDSettingsApiKeyHelpText": "Ключ API с сайта (Находится в Моя безопасность => Ключ API)",
|
||||
"IndexerBeyondHDSettingsSearchTypes": "Типы поиска",
|
||||
"IndexerBeyondHDSettingsSearchTypesHelpText": "Выберите типы релизов, которые вас интересуют. Если ничего не выбрано, используются все варианты.",
|
||||
"IndexerFileListSettingsPasskeyHelpText": "Passkey сайта (Это алфавитно-цифровая строка в URL трекера, отображаемая в вашем клиенте загрузки)",
|
||||
"IndexerGazelleGamesSettingsSearchGroupNames": "Поиск названий групп",
|
||||
"IndexerIPTorrentsSettingsCookieUserAgentHelpText": "User-Agent, используемый с cookie из браузера",
|
||||
"IndexerIPTorrentsSettingsFreeleechOnlyHelpText": "Искать только релизы freeleech",
|
||||
"AreYouSureYouWantToDeleteIndexer": "Вы уверены, что хотите удалить '{name}' из {appName}?",
|
||||
"BookSearch": "Поиск книг",
|
||||
"BookSearchTypes": "Типы поиска книг",
|
||||
"IndexerPassThePopcornSettingsFreeleechOnlyHelpText": "Искать только релизы freeleech",
|
||||
"IndexerSettingsGrabLimitHelpText": "Максимальное количество захватов, указанное соответствующей единицей, которое {appName} будет разрешать для сайта",
|
||||
"IndexerSettingsLimitsUnit": "Единица лимита",
|
||||
"IndexerSettingsLimitsUnitHelpText": "Единица времени для определения лимитов каждого индексатора",
|
||||
"IndexerSettingsQueryLimit": "Лимит запросов",
|
||||
"IndexerSettingsRssKey": "Ключ RSS",
|
||||
"Parameters": "Параметры",
|
||||
"RedirectHelpText": "Перенаправить входящий запрос на загрузку для индексатора и передать захват напрямую, вместо проксирования запроса через {appName}.",
|
||||
"SettingsFilterSentryEvents": "Фильтровать события аналитики",
|
||||
"NotSupported": "Не поддерживается",
|
||||
"SyncLevel": "Уровень синхронизации",
|
||||
"UISettingsSummary": "Параметры для людей с ограниченными возможностями: дата, язык и цветовая схема",
|
||||
"DeleteApplication": "Удалить приложение",
|
||||
"Description": "Описание",
|
||||
"IndexerQuery": "Запрос индексатора",
|
||||
"IndexerSite": "Сайт индексатора",
|
||||
"IndexerTagsHelpText": "Используйте теги для указания прокси индексатора или приложений, синхронизированных с индексатором.",
|
||||
"QueryType": "Тип запроса",
|
||||
"MovieSearch": "Поиск фильма",
|
||||
"RssFeed": "Лента RSS",
|
||||
"AppsMinimumSeeders": "Приложения Минимальное количество сидеров",
|
||||
"ConnectSettingsSummary": "Уведомления и пользовательские скрипты",
|
||||
"DeleteClientCategory": "Удалить категорию клиента загрузки",
|
||||
"IndexerAlreadySetup": "Как минимум один индексатор уже настроен",
|
||||
"IndexerCategories": "Категории индексатора",
|
||||
"IndexerProxy": "Прокси индексатора",
|
||||
"IndexerRss": "RSS индексатора",
|
||||
"Public": "Публичный",
|
||||
"SettingsLogRotate": "Ротация журналов",
|
||||
"SettingsLogRotateHelpText": "Максимальное количество сохраняемых файлов в каталоге журналов",
|
||||
"DisabledUntil": "Отключено до",
|
||||
"SearchType": "Тип поиска",
|
||||
"AppSettingsSummary": "Приложения и настройки для управления взаимодействием {appName} с вашими программами PVR",
|
||||
"SettingsFilterSentryEventsHelpText": "Фильтровать известные события ошибок пользователя, чтобы они не отправлялись в виде аналитики.",
|
||||
"MovieSearchTypes": "Типы поиска фильма",
|
||||
"Apps": "Программы",
|
||||
"Auth": "Авторизация",
|
||||
"ClearHistoryMessageText": "Вы уверены, что хотите очистить всю историю {appName}?",
|
||||
"DeleteSelectedIndexer": "Удалить выбранный индексатор",
|
||||
"DownloadClientCategory": "Категория клиента загрузки",
|
||||
"IndexerHDBitsSettingsPasskeyHelpText": "Passkey из пользовательских данных",
|
||||
"SettingsIndexerLogging": "Включить индексатор",
|
||||
"RepeatSearch": "Повторить поиск",
|
||||
"AddCategory": "Добавить категорию",
|
||||
"AdvancedSettingsShownClickToHide": "Расширенные настройки видны, нажмите, чтобы скрыть",
|
||||
"AppProfileInUse": "Профиль приложения в использовании",
|
||||
"IndexerAlphaRatioSettingsExcludeScene": "Исключить SCENE",
|
||||
"IndexerAlphaRatioSettingsExcludeSceneHelpText": "Исключить релизы SCENE из результатов",
|
||||
"IndexerBeyondHDSettingsRewindOnly": "Только повторы",
|
||||
"IndexerDetails": "Подробности индексатора",
|
||||
"EditCategory": "Редактировать категорию",
|
||||
"FoundCountReleases": "Найдено релизов: {itemCount}",
|
||||
"IndexerAlphaRatioSettingsFreeleechOnlyHelpText": "Искать только релизы freeleech",
|
||||
"IndexerBeyondHDSettingsLimitedOnly": "Только лимитированные",
|
||||
"IndexerBeyondHDSettingsRefundOnlyHelpText": "Искать только возвраты",
|
||||
"IndexerBeyondHDSettingsRewindOnlyHelpText": "Искать только повторы",
|
||||
"IndexerHistoryLoadError": "Ошибка загрузки истории индексатора",
|
||||
"IndexerNebulanceSettingsApiKeyHelpText": "Ключ API из Настроек пользователя > Ключи API. Ключ должен иметь права на просмотр и загрузку",
|
||||
"IndexerPassThePopcornSettingsApiKeyHelpText": "Ключ API сайта",
|
||||
"SettingsConsoleLogLevel": "Уровень журналирования консоли",
|
||||
"IndexerSettingsGrabLimit": "Лимит захвата",
|
||||
"IndexerSettingsVipExpiration": "Дата окончания VIP",
|
||||
"SearchQueries": "Запросы поиска",
|
||||
"SeedRatioHelpText": "Коэффициент, которого должен достичь торрент перед остановкой, пусто - значение по умолчанию приложения",
|
||||
"NoIndexerCategories": "Нет категорий для этого индексатора",
|
||||
"IndexerVipExpiredHealthCheckMessage": "Привилегии VIP для индексатора истекли: {indexerNames}",
|
||||
"DefaultCategory": "Категория по умолчанию",
|
||||
"IndexerHDBitsSettingsFreeleechOnlyHelpText": "Показать только релизы freeleech",
|
||||
"IndexerHDBitsSettingsOrigins": "Источники",
|
||||
"IndexerNzbIndexSettingsApiKeyHelpText": "Ключ API сайта",
|
||||
"IndexerOrpheusSettingsApiKeyHelpText": "API ключ сайта (Находится в Настройки => Настройки доступа)",
|
||||
"IndexerSettingsApiUser": "Пользователь API",
|
||||
"IndexerSettingsAppsMinimumSeeders": "Приложения Минимальное количество сидеров",
|
||||
"RawSearchSupported": "Поддерживается необработанный поиск",
|
||||
"RssQueries": "Запросы RSS",
|
||||
"SearchCapabilities": "Возможности поиска",
|
||||
"SearchIndexers": "Поисковые индексаторы",
|
||||
"ActiveIndexers": "Активные индексаторы",
|
||||
"ApplicationsLoadError": "Не удалось загрузить список приложений",
|
||||
"IncludeManualGrabsHelpText": "Включить ручные захваты, сделанные внутри {appName}",
|
||||
"NoApplicationsFound": "Приложения не найдены",
|
||||
"IndexerHDBitsSettingsUseFilenamesHelpText": "Выберите этот вариант, если хотите использовать имена файлов торрента в качестве названий релизов",
|
||||
"IndexerHDBitsSettingsUsernameHelpText": "Имя пользователя сайта",
|
||||
"IndexerSettingsCookieHelpText": "Cookie сайта",
|
||||
"IndexerSettingsFreeleechOnly": "Только Freeleech",
|
||||
"IndexerSettingsPasskey": "Pass Key",
|
||||
"IndexerId": "ID индексатора",
|
||||
"DeleteSelectedIndexers": "Удалить выбранные индексаторы",
|
||||
"IndexerGazelleGamesSettingsApiKeyHelpTextWarning": "Требуются разрешения 'Пользователь' и 'Торренты'",
|
||||
"IndexerNewznabSettingsAdditionalParametersHelpText": "Дополнительные параметры Newznab",
|
||||
"IndexerNewznabSettingsApiKeyHelpText": "Ключ API сайта",
|
||||
"IndexerSettingsBaseUrl": "Базовый URL-адрес",
|
||||
"IndexerSettingsAppsMinimumSeedersHelpText": "Минимальное количество сидеров, необходимое для приложений, чтобы индексатор мог скачать, пустое значение - значение профиля синхронизации по умолчанию",
|
||||
"PackSeedTimeHelpText": "Время, в течение которого торрент пакета (сезон или дискография) должен оставаться на раздаче перед остановкой, пусто — используется значение приложения по умолчанию",
|
||||
"IndexerNoDefinitionCheckHealthCheckMessage": "Индексаторы {indexerNames} не имеют определения и не работают. Удалите их из {appName} и добавьте снова.",
|
||||
"AuthQueries": "Запросы авторизации",
|
||||
"IndexerNewznabSettingsVipExpirationHelpText": "Введите дату (yyyy-mm-dd) окончания VIP или оставьте поле пустым, {appName} оповестит вас за неделю до окончания VIP",
|
||||
"IndexerPassThePopcornSettingsApiUserHelpText": "Эти настройки расположены в разделе безопасности PassThePopcorn (Редактировать профиль > Безопасность).",
|
||||
"IndexerSettingsBaseUrlHelpText": "Выберите основной URL-адрес, который {appName} будет использовать для запросов на сайт",
|
||||
"Open": "Открыть",
|
||||
"IndexerInfo": "Информация об индексаторе",
|
||||
"SettingsLogSql": "Журналировать SQL",
|
||||
"AverageResponseTimesMs": "Среднее время ответа индексатора (мс)",
|
||||
"CountIndexersAvailable": "Доступно индексаторов: {count}",
|
||||
"IndexerSettingsSummary": "Конфигурация глобальных настроек индексатора, включая прокси.",
|
||||
"IndexerTagsHelpTextWarning": "Теги следует использовать с осторожностью, они могут иметь непредвиденные последствия. Индексатор с тегом будет синхронизироваться только с приложениями, имеющими тот же тег.",
|
||||
"IndexerTorrentSyndikatSettingsApiKeyHelpText": "Ключ API сайта",
|
||||
"NoIndexerHistory": "Нет истории для этого индексатора",
|
||||
"AppsMinimumSeedersHelpText": "Минимальное количество сидеров, необходимое для приложений, чтобы индексатор мог скачать, пустое значение - значение профиля синхронизации по умолчанию",
|
||||
"DownloadClientsSettingsSummary": "Настройки клиентов загрузки для интеграции в интерфейс поиска {appName}",
|
||||
"Proxies": "Прокси",
|
||||
"AppProfileSelectHelpText": "Профили приложения используются для управления настройками RSS, автоматического поиска и интерактивного поиска при синхронизации приложения",
|
||||
"ProwlarrSupportsAnyDownloadClient": "{appName} совместим с любым из перечисленных ниже клиентом загрузки.",
|
||||
"Query": "Запрос",
|
||||
"QueryResults": "Результаты запроса",
|
||||
"ActiveApps": "Активные приложения",
|
||||
"ApplicationTagsHelpText": "Синхронизировать индексаторы с этим приложением, которые имеют один или более совпадающих тегов. Если здесь не перечислены теги, то все индексаторы будут синхронизированы без ограничений.",
|
||||
"ApplicationTagsHelpTextWarning": "Теги следует использовать с осторожностью, они могут иметь непредвиденные последствия. Приложение с тегом будет синхронизироваться только с индексаторами, имеющими тот же тег.",
|
||||
"IndexerGazelleGamesSettingsApiKeyHelpText": "API ключ сайта (Находится в Настройки => Настройки доступа)",
|
||||
"IndexerGazelleGamesSettingsSearchGroupNamesHelpText": "Поиск релизов по названию групп",
|
||||
"IndexerHDBitsSettingsUseFilenames": "Использовать имена файлов",
|
||||
"IndexerRedactedSettingsApiKeyHelpText": "API ключ сайта (Находится в Настройки => Настройки доступа)",
|
||||
"IndexerSettingsPackSeedTime": "Время раздачи пакета",
|
||||
"IndexerSettingsPackSeedTimeIndexerHelpText": "Время, в течение которого торрент пакета (сезон или дискография) должен оставаться на раздаче перед остановкой, пусто — используется значение приложения по умолчанию",
|
||||
"ClickToChangeQueryOptions": "Нажмите, чтобы изменить параметры запроса",
|
||||
"DownloadClientSettingsDefaultCategorySubFolderHelpText": "Категория по умолчанию, если для релиза нет соответствующей категории. Создание определённой категории для {appName}, позволяет избежать конфликтов с загрузками, не связанными с {appName}. Использование категории не обязательно, но настоятельно рекомендуется. Создаёт подкаталог [category] в каталоге вывода.",
|
||||
"AverageQueries": "Среднее количество запросов",
|
||||
"IndexerGazelleGamesSettingsFreeleechOnlyHelpText": "Искать только релизы freeleech",
|
||||
"IndexerMTeamTpSettingsApiKeyHelpText": "Ключ API сайта (Находится в Панели управления пользователя => Безопасность => Лаборатория)",
|
||||
"IndexerMTeamTpSettingsFreeleechOnlyHelpText": "Искать только релизы freeleech",
|
||||
"ProwlarrDownloadClientsAlert": "Если вы намерены выполнять поиск непосредственно в {appName}, вам необходимо добавить клиенты загрузки. В противном случае, добавлять их здесь не нужно. Для поиска из ваших приложений используются клиенты загрузки, которые настроены в самих приложениях.",
|
||||
"ProwlarrDownloadClientsInAppOnlyAlert": "Клиенты загрузки предназначены только для поиска внутри приложения {appName} и не синхронизируются с другими приложениями. Мы не планируем добавлять функцию синхронизации.",
|
||||
"ProwlarrSupportsAnyIndexer": "{appName} поддерживает множество индексаторов, а также любой индексатор, использующий стандарт Newznab/Torznab, с помощью 'Generic Newznab' (для Usenet) или 'Generic Torznab' (для торрентов). Выберите и добавьте свой индексатор из списка ниже.",
|
||||
"Redirected": "Перенаправлено",
|
||||
"DownloadClientSettingsDefaultCategoryHelpText": "Категория по умолчанию, если для релиза нет соответствующей категории. Создание определённой категории для {appName}, позволяет избежать конфликтов с загрузками, не связанными с {appName}. Использование категории не обязательно, но настоятельно рекомендуется.",
|
||||
"DownloadClientSettingsPriorityItemHelpText": "Приоритет, используемый при захвате элементов",
|
||||
"GrabTitle": "Захватить название",
|
||||
"IndexerBeyondHDSettingsFreeleechOnlyHelpText": "Искать только релизы freeleech",
|
||||
"IndexerBeyondHDSettingsLimitedOnlyHelpText": "Искать только freeleech (Лимитированная отдача)",
|
||||
"IndexerBeyondHDSettingsRefundOnly": "Только возврат",
|
||||
"IndexerBeyondHDSettingsRssKeyHelpText": "Ключ RSS с сайта (Находится в Моя безопасность => Ключ RSS)",
|
||||
"IndexerDownloadClientHelpText": "Определите клиент загрузки, используемый для загрузки из этого индексатора в {appName}.",
|
||||
"NewznabUrl": "URL-адрес Newznab",
|
||||
"SeedTimeHelpText": "Время, в течение которого торрент должен оставаться на раздаче перед остановкой, пусто — используется значение приложения по умолчанию",
|
||||
"IndexerFileListSettingsFreeleechOnlyHelpText": "Искать только релизы freeleech",
|
||||
"IndexerFileListSettingsUsernameHelpText": "Имя пользователя сайта",
|
||||
"IndexerHealthCheckNoIndexers": "Нет включённых индексаторов, {appName} не будет возвращать результаты поиска",
|
||||
"IndexerObsoleteCheckMessage": "Индексаторы: {0} устарели или были обновлены. Удалите их из {appName} и (или) добавьте снова",
|
||||
"IndexerSettingsQueryLimitHelpText": "Максимальное количество запросов, указанное соответствующей единицей, которое {appName} будет разрешать для сайта",
|
||||
"IndexerVipExpiringHealthCheckMessage": "Привилегии VIP для индексатора скоро истекут: {indexerNames}",
|
||||
"MinimumSeedersHelpText": "Минимальное количество сидеров, необходимое приложению для захвата индексатором",
|
||||
"SelectedCountOfCountReleases": "Выбрано {selectedCount} из {itemCount} релизов",
|
||||
"SemiPrivate": "Частично приватный",
|
||||
"SettingsIndexerLoggingHelpText": "Журналировать дополнительные данные индексатора, включая ответ",
|
||||
"SyncAppIndexers": "Синхронизировать индексаторы приложения",
|
||||
"UnableToLoadAppProfiles": "Не удалось загрузить профили приложения",
|
||||
"Website": "Веб-сайт",
|
||||
"TvSearch": "Поиск ТВ-программ",
|
||||
"Url": "URL-адрес",
|
||||
"UnableToLoadIndexerProxies": "Не удалось загрузить прокси индексатора",
|
||||
"UnableToLoadDevelopmentSettings": "Не удалось загрузить настройки разработки",
|
||||
"VipExpiration": "Дата окончания VIP",
|
||||
"IndexerIPTorrentsSettingsCookieUserAgent": "Cookie User-Agent",
|
||||
"AverageGrabs": "Среднее количество захватов"
|
||||
}
|
||||
|
||||
@@ -518,7 +518,7 @@
|
||||
"OnApplicationUpdateHelpText": "Uygulama Güncellemesinde",
|
||||
"DeleteSelectedApplicationsMessageText": "Seçilen {count} içe aktarma listesini silmek istediğinizden emin misiniz?",
|
||||
"ProxyValidationBadRequest": "Proxy ile test edilemedi. DurumKodu: {statusCode}",
|
||||
"UpdateAvailableHealthCheckMessage": "Yeni güncelleme mevcut",
|
||||
"UpdateAvailableHealthCheckMessage": "Yeni güncelleme mevcut: {version}",
|
||||
"days": "gün",
|
||||
"Default": "Varsayılan",
|
||||
"GrabRelease": "Yayın Yakalama",
|
||||
@@ -534,5 +534,7 @@
|
||||
"Any": "Herhangi",
|
||||
"AllSearchResultsHiddenByFilter": "Tüm sonuçlar, uygulanan filtre tarafından gizlenir",
|
||||
"HealthMessagesInfoBox": "Satırın sonundaki wiki bağlantısını (kitap simgesi) tıklayarak veya [günlüklerinizi]({link}) kontrol ederek bu durum kontrolü mesajlarının nedeni hakkında daha fazla bilgi bulabilirsiniz. Bu mesajları yorumlamakta zorluk yaşıyorsanız aşağıdaki bağlantılardan destek ekibimize ulaşabilirsiniz.",
|
||||
"PackageVersionInfo": "{packageAuthor} tarafından {packageVersion}"
|
||||
"PackageVersionInfo": "{packageAuthor} tarafından {packageVersion}",
|
||||
"LogSizeLimit": "Log Boyutu Sınırı",
|
||||
"LogSizeLimitHelpText": "Arşivlemeden önce MB cinsinden maksimum log dosya boyutu. Varsayılan 1 MB'tır."
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"About": "关于",
|
||||
"AcceptConfirmationModal": "接受确认模组",
|
||||
"AcceptConfirmationModal": "接受确认对话框",
|
||||
"Actions": "动作",
|
||||
"Add": "添加",
|
||||
"AddApplication": "添加应用程序",
|
||||
@@ -21,10 +21,10 @@
|
||||
"AllIndexersHiddenDueToFilter": "由于应用了筛选器,所有索引器都被隐藏。",
|
||||
"Analytics": "分析",
|
||||
"AnalyticsEnabledHelpText": "将匿名使用情况和错误信息发送到{appName}的服务器。这包括有关您的浏览器的信息、您使用的{appName} WebUI页面、错误报告以及操作系统和运行时版本。我们将使用此信息来确定功能和错误修复的优先级。",
|
||||
"ApiKey": "接口密钥 (API Key)",
|
||||
"ApiKeyValidationHealthCheckMessage": "请将API密钥更新为至少{length}个字符长。您可以通过设置或配置文件执行此操作",
|
||||
"AppDataDirectory": "AppData目录",
|
||||
"AppDataLocationHealthCheckMessage": "正在更新期间的 AppData 不会被更新删除",
|
||||
"ApiKey": "API 密钥",
|
||||
"ApiKeyValidationHealthCheckMessage": "请将API密钥更新为至少 {length} 个字符长。您可以通过设置或配置文件完成此操作",
|
||||
"AppDataDirectory": "AppData 目录",
|
||||
"AppDataLocationHealthCheckMessage": "更新时无法阻止删除 AppData",
|
||||
"AppProfileInUse": "正在使用的应用程序配置文件",
|
||||
"AppProfileSelectHelpText": "应用程序配置用于控制应用程序同步设置 RSS、自动搜索和交互式搜索设置",
|
||||
"AppSettingsSummary": "配置{appName}与PVR程序交互方式的应用和设置",
|
||||
@@ -34,7 +34,7 @@
|
||||
"ApplicationStatusCheckAllClientMessage": "由于故障所用应用程序都不可用",
|
||||
"ApplicationStatusCheckSingleClientMessage": "由于故障应用程序不可用",
|
||||
"ApplicationURL": "应用程序 URL",
|
||||
"ApplicationUrlHelpText": "此应用的外部URL,包含 http(s)://、端口和基本URL",
|
||||
"ApplicationUrlHelpText": "此应用的外部 URL,包含 http(s)://、端口和基本 URL",
|
||||
"Applications": "程序",
|
||||
"Apply": "应用",
|
||||
"ApplyTags": "应用标签",
|
||||
@@ -45,9 +45,9 @@
|
||||
"Auth": "认证",
|
||||
"Authentication": "认证",
|
||||
"AuthenticationMethodHelpText": "需要用户名和密码以访问 {appName}",
|
||||
"AuthenticationRequired": "需要身份验证",
|
||||
"AuthenticationRequiredHelpText": "修改哪些请求需要认证。除非你了解其中的风险,否则不要更改。",
|
||||
"AuthenticationRequiredWarning": "为了防止未经身份验证的远程访问,{appName} 现在需要启用身份验证。您可以禁用本地地址的身份验证。",
|
||||
"AuthenticationRequired": "需要认证",
|
||||
"AuthenticationRequiredHelpText": "修改这些请求需要认证。除非你了解其中的风险,否则不要进行更改。",
|
||||
"AuthenticationRequiredWarning": "为防止未经认证的远程访问,{appName} 现需要启用身份认证。您可以选择禁用本地地址的身份认证。",
|
||||
"Author": "作者",
|
||||
"Automatic": "自动化",
|
||||
"AutomaticSearch": "自动搜索",
|
||||
@@ -194,7 +194,7 @@
|
||||
"IndexerDetails": "索引器详细信息",
|
||||
"IndexerDisabled": "索引器已被禁用",
|
||||
"IndexerFailureRate": "Indexer失败率",
|
||||
"IndexerFlags": "搜刮器标记",
|
||||
"IndexerFlags": "索引器标志",
|
||||
"IndexerHealthCheckNoIndexers": "未启用任何搜刮器,{appName}将不会返回搜索结果",
|
||||
"IndexerInfo": "索引器信息",
|
||||
"IndexerLongTermStatusAllUnavailableHealthCheckMessage": "由于故障超过6小时,所有搜刮器均不可用",
|
||||
@@ -322,7 +322,7 @@
|
||||
"Queue": "队列",
|
||||
"Queued": "队列中",
|
||||
"Rss": "RSS",
|
||||
"RssIsNotSupportedWithThisIndexer": "该搜刮器不支持RSS",
|
||||
"RssIsNotSupportedWithThisIndexer": "该索引器不支持 RSS",
|
||||
"RawSearchSupported": "支持原始搜索",
|
||||
"ReadTheWikiForMoreInformation": "查阅Wiki获得更多信息",
|
||||
"Reddit": "Reddit",
|
||||
@@ -425,7 +425,7 @@
|
||||
"TestAll": "测试全部",
|
||||
"TestAllApps": "测试全部应用",
|
||||
"TestAllClients": "测试全部客户端",
|
||||
"TestAllIndexers": "测试全部搜刮器",
|
||||
"TestAllIndexers": "测试全部索引器",
|
||||
"TheLatestVersionIsAlreadyInstalled": "已安装最新版本的{appName}",
|
||||
"Theme": "主题",
|
||||
"ThemeHelpText": "更改应用程序UI主题,“自动”主题将使用您的操作系统主题设置亮或暗模式。灵感来源于{inspirredby}。",
|
||||
@@ -484,7 +484,7 @@
|
||||
"Warn": "警告",
|
||||
"Website": "网站",
|
||||
"Wiki": "Wiki",
|
||||
"Year": "年",
|
||||
"Year": "年份",
|
||||
"Yes": "确定",
|
||||
"YesCancel": "确定,取消",
|
||||
"Yesterday": "昨天",
|
||||
@@ -493,9 +493,9 @@
|
||||
"ApplyChanges": "应用更改",
|
||||
"ApplyTagsHelpTextAdd": "添加: 添加标签至已有的标签列表中",
|
||||
"CountDownloadClientsSelected": "已选择 {count} 个下载客户端",
|
||||
"CountIndexersSelected": "已选择 {count} 个索引器",
|
||||
"CountIndexersSelected": "选定 {count} 个索引器",
|
||||
"DeleteSelectedDownloadClientsMessageText": "您确定要删除 {count} 个选定的下载客户端吗?",
|
||||
"DeleteSelectedIndexersMessageText": "您确定要删除{count}选定的索引器吗?",
|
||||
"DeleteSelectedIndexersMessageText": "您确定要删除选定的 {count} 个索引器吗?",
|
||||
"EditSelectedDownloadClients": "编辑选定的下载客户端",
|
||||
"Implementation": "执行",
|
||||
"ManageDownloadClients": "管理下载客户端",
|
||||
@@ -503,12 +503,12 @@
|
||||
"NoIndexersFound": "未找到索引器",
|
||||
"SelectIndexers": "搜刮器搜索",
|
||||
"ApplyTagsHelpTextHowToApplyApplications": "如何给选中的电影添加标签",
|
||||
"ApplyTagsHelpTextHowToApplyIndexers": "如何将标签应用到已选择的索引器",
|
||||
"ApplyTagsHelpTextHowToApplyIndexers": "如何将标签应用到已选中的索引器",
|
||||
"ApplyTagsHelpTextReplace": "替换: 用输入的标签替换当前标签 (不输入将会清除所有标签)",
|
||||
"DeleteSelectedApplicationsMessageText": "您确定要删除{count}选定的应用程序吗?",
|
||||
"DeleteSelectedDownloadClients": "删除下载客户端",
|
||||
"DownloadClientPriorityHelpText": "优先考虑多个下载客户端,循环查询用于具有相同优先级的客户端。",
|
||||
"EditSelectedIndexers": "编辑选定的索引器",
|
||||
"EditSelectedIndexers": "编辑选定索引器",
|
||||
"OnHealthRestored": "健康度恢复",
|
||||
"OnHealthRestoredHelpText": "健康度恢复",
|
||||
"ApplyTagsHelpTextRemove": "移除: 移除已输入的标签",
|
||||
@@ -559,11 +559,11 @@
|
||||
"AddConnectionImplementation": "添加连接- {implementationName}",
|
||||
"AddDownloadClientImplementation": "添加下载客户端- {implementationName}",
|
||||
"AddIndexerProxyImplementation": "添加搜刮器代理-{实体名称}",
|
||||
"AppUpdated": "{appName} 升级",
|
||||
"AppUpdated": "{appName} 已升级",
|
||||
"EditDownloadClientImplementation": "编辑下载客户端- {implementationName}",
|
||||
"EditIndexerImplementation": "编辑索引器- {implementationName}",
|
||||
"EditIndexerProxyImplementation": "添加搜刮器代理-{实体名称}",
|
||||
"AppUpdatedVersion": "{appName} 已经更新到 {version} 版本,重新加载 {appName} 使更新生效",
|
||||
"AppUpdatedVersion": "{appName} 已经更新到版本 {version} ,重新加载 {appName} 使更新生效",
|
||||
"EditApplicationImplementation": "添加应用-{实体名称}",
|
||||
"EditConnectionImplementation": "编辑连接- {implementationName}",
|
||||
"NotificationStatusAllClientHealthCheckMessage": "由于故障所用应用程序都不可用",
|
||||
@@ -571,7 +571,7 @@
|
||||
"AuthBasic": "基础(浏览器弹出对话框)",
|
||||
"AuthForm": "表单(登陆页面)",
|
||||
"AuthenticationMethod": "认证方式",
|
||||
"AuthenticationMethodHelpTextWarning": "请选择一个有效的身份验证方式",
|
||||
"AuthenticationMethodHelpTextWarning": "请选择一个有效的认证方式",
|
||||
"AuthenticationRequiredPasswordHelpTextWarning": "请输入新密码",
|
||||
"AuthenticationRequiredUsernameHelpTextWarning": "请输入新用户名",
|
||||
"Clone": "复制",
|
||||
@@ -580,7 +580,7 @@
|
||||
"External": "外部的",
|
||||
"None": "无",
|
||||
"ResetAPIKeyMessageText": "您确定要重置您的 API 密钥吗?",
|
||||
"IndexerDownloadClientHealthCheckMessage": "有无效下载客户端的索引器:{indexerNames}。",
|
||||
"IndexerDownloadClientHealthCheckMessage": "使用无效下载客户端的索引器:{indexerNames}。",
|
||||
"ApplicationTagsHelpTextWarning": "标签应该谨慎使用,它们可能会产生意想不到的效果。带有标签的应用程序只会与具有相同标签的索引器同步。",
|
||||
"EditCategory": "编辑分类",
|
||||
"IndexerHistoryLoadError": "加载索引器历史记录出错",
|
||||
@@ -603,7 +603,7 @@
|
||||
"NoIndexerCategories": "没有找到此索引器的分类",
|
||||
"DownloadClientQbittorrentSettingsContentLayout": "内容布局",
|
||||
"DownloadClientQbittorrentSettingsContentLayoutHelpText": "是否使用 qBittorrent 配置的内容布局,使用种子的原始布局或始终创建子文件夹(qBittorrent 4.3.2+)",
|
||||
"DownloadClientAriaSettingsDirectoryHelpText": "可选的下载位置,留空使用 Aria2 默认位置",
|
||||
"DownloadClientAriaSettingsDirectoryHelpText": "下载位置可选择,留空使用 Aria2 默认位置",
|
||||
"ManageClients": "管理客户端",
|
||||
"CustomFilter": "自定义过滤器",
|
||||
"BlackholeFolderHelpText": "{appName} 将在其中存储 {extension} 文件的文件夹",
|
||||
@@ -616,7 +616,7 @@
|
||||
"DownloadClientFloodSettingsAdditionalTags": "附加标签",
|
||||
"DownloadClientFreeboxSettingsApiUrl": "API 地址",
|
||||
"DownloadClientFreeboxSettingsAppId": "App ID",
|
||||
"DownloadClientDelugeSettingsUrlBaseHelpText": "向 deluge json url 添加前缀,请参阅 {url}",
|
||||
"DownloadClientDelugeSettingsUrlBaseHelpText": "向 Deluge JSON URL 添加前缀,请参阅 {url}",
|
||||
"DownloadClientFloodSettingsUrlBaseHelpText": "为 Flood API 添加前缀,例如 {url}",
|
||||
"DownloadClientFreeboxSettingsAppToken": "App Token",
|
||||
"DownloadClientFreeboxSettingsAppTokenHelpText": "创建访问 Freebox API 所需的 App token(即“ app_token”)",
|
||||
@@ -639,7 +639,7 @@
|
||||
"SecretToken": "密钥令牌",
|
||||
"XmlRpcPath": "XML RPC 路径",
|
||||
"ApplicationSettingsSyncRejectBlocklistedTorrentHashesHelpText": "如果 torrent 的哈希被屏蔽了,某些索引器在使用RSS或者搜索期间可能无法正确拒绝它,启用此功能将允许在抓取 torrent 之后但在将其发送到客户端之前拒绝它。",
|
||||
"DownloadClientDownloadStationSettingsDirectoryHelpText": "用于存放下载内容的可选共享文件夹,留空以使用默认的 Download Station 位置",
|
||||
"DownloadClientDownloadStationSettingsDirectoryHelpText": "用于存放下载内容的共享文件夹可选择,留空使用默认的 Download Station 位置",
|
||||
"DownloadClientFloodSettingsAdditionalTagsHelpText": "添加媒体属性作为标签。 提示是示例。",
|
||||
"DownloadClientFreeboxSettingsApiUrlHelpText": "使用 API 版本定义 Freebox API 基本 URL,例如“{url}”,默认为“{defaultApiUrl}”",
|
||||
"DownloadClientPneumaticSettingsStrmFolder": "Strm 文件夹",
|
||||
@@ -680,10 +680,10 @@
|
||||
"IndexerSettingsCookie": "Cookie",
|
||||
"ProxyValidationBadRequest": "测试代理失败。状态码:{statusCode}",
|
||||
"ProxyValidationUnableToConnect": "无法连接到索引器:{exceptionMessage}。 检查有关此错误的日志以了解详细信息",
|
||||
"DownloadClientFloodSettingsTagsHelpText": "下载的初始标签。 要被识别,下载必须具有所有初始标签。 这可以避免与不相关的下载发生冲突。",
|
||||
"DownloadClientFloodSettingsTagsHelpText": "下载的初始标签。下载必须具有所有初始标签才可被识别。 这可以避免与不相关的下载发生冲突。",
|
||||
"DownloadClientPneumaticSettingsStrmFolderHelpText": "该文件夹中的 .strm 文件将由 drone 导入",
|
||||
"IndexerHDBitsSettingsCodecsHelpText": "如果未指定,则使用所有选项。",
|
||||
"IndexerSettingsSeedRatioHelpText": "种子在停止之前应达到的比率,留空使用下载客户端的默认值。 比率应至少为 1.0 并遵循索引器规则",
|
||||
"IndexerSettingsSeedRatioHelpText": "停止之前应达到的做种比率,留空使用下载客户端的默认值。 比率应至少为 1.0 并遵循索引器规则",
|
||||
"IndexerSettingsSeedTimeHelpText": "停止前应做种的时间,留空使用下载客户端的默认值",
|
||||
"TorrentBlackholeSaveMagnetFiles": "保存磁力链接文件",
|
||||
"TorrentBlackholeTorrentFolder": "种子文件夹",
|
||||
@@ -697,5 +697,19 @@
|
||||
"Redirected": "重定向",
|
||||
"AllSearchResultsHiddenByFilter": "根据过滤条件所有结果已隐藏",
|
||||
"HealthMessagesInfoBox": "您可以通过单击行尾的wiki链接(图书图标)或检查[日志]({link})来查找有关这些运行状况检查消息原因的更多信息。如果你在理解这些信息方面有困难,你可以通过下面的链接联系我们的支持。",
|
||||
"PackageVersionInfo": "{packageVersion} 由 {packageAuthor} 制作"
|
||||
"PackageVersionInfo": "{packageVersion} 由 {packageAuthor} 制作",
|
||||
"LabelIsRequired": "需要标签",
|
||||
"LogSizeLimit": "日志大小限制",
|
||||
"LogSizeLimitHelpText": "存档前的最大日志文件大小(MB)。默认值为 1 MB。",
|
||||
"NotificationsTelegramSettingsIncludeAppName": "标题中包含 {appName}",
|
||||
"NotificationsTelegramSettingsIncludeAppNameHelpText": "可选,在消息标题前加上 {appName} 以区分来自不同应用的通知",
|
||||
"NotificationsEmailSettingsUseEncryption": "启用加密",
|
||||
"NotificationsEmailSettingsUseEncryptionHelpText": "是否优先使用加密(如果服务器已配置),始终使用通过SSL(仅端口465)或StartTLS(任何其他端口)进行加密,或从不使用加密",
|
||||
"ClickToChangeQueryOptions": "单击以更改查询选项",
|
||||
"ApplicationsLoadError": "无法加载应用程序列表",
|
||||
"DownloadClientSettingsDefaultCategoryHelpText": "默认的备用分类,当发布资源没有匹配的分类时将使用此分类。为 {appName} 添加一个特定的分类,可以避免与非 {appName} 的无关下载发生冲突。分类是可选的,但强烈建议使用。",
|
||||
"AverageGrabs": "平均抓取次数",
|
||||
"AverageQueries": "平均查询次数",
|
||||
"DefaultCategory": "默认分类",
|
||||
"DownloadClientSettingsDefaultCategorySubFolderHelpText": "默认的备用分类,当发布资源没有匹配的分类时将使用此分类。为 {appName} 添加一个特定的分类,可以避免与非 {appName} 的无关下载发生冲突。分类是可选的,但强烈建议使用。启用分类后,会在输出目录中创建一个 [分类] 子目录。"
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AngleSharp.Xml" Version="1.0.0" />
|
||||
<PackageReference Include="Dapper" Version="2.0.151" />
|
||||
<PackageReference Include="Diacritical.Net" Version="1.0.4" />
|
||||
<PackageReference Include="MailKit" Version="3.6.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="6.0.32" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" />
|
||||
|
||||
@@ -127,10 +127,12 @@ namespace NzbDrone.Core.ThingiProvider
|
||||
|
||||
public virtual IEnumerable<TProviderDefinition> Update(IEnumerable<TProviderDefinition> definitions)
|
||||
{
|
||||
_providerRepository.UpdateMany(definitions.ToList());
|
||||
_eventAggregator.PublishEvent(new ProviderBulkUpdatedEvent<TProvider>(definitions));
|
||||
var providerDefinitions = definitions.ToList();
|
||||
|
||||
return definitions;
|
||||
_providerRepository.UpdateMany(providerDefinitions);
|
||||
_eventAggregator.PublishEvent(new ProviderBulkUpdatedEvent<TProvider>(providerDefinitions));
|
||||
|
||||
return providerDefinitions;
|
||||
}
|
||||
|
||||
public void Delete(int id)
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace NzbDrone.Host
|
||||
Name = "apikey",
|
||||
Type = SecuritySchemeType.ApiKey,
|
||||
Scheme = "apiKey",
|
||||
Description = "Apikey passed as header",
|
||||
Description = "Apikey passed as query parameter",
|
||||
In = ParameterLocation.Query,
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
|
||||
@@ -25,7 +25,13 @@ namespace Prowlarr.Api.V1.History
|
||||
public PagingResource<HistoryResource> GetHistory([FromQuery] PagingRequestResource paging, [FromQuery(Name = "eventType")] int[] eventTypes, bool? successful, string downloadId, [FromQuery] int[] indexerIds = null)
|
||||
{
|
||||
var pagingResource = new PagingResource<HistoryResource>(paging);
|
||||
var pagingSpec = pagingResource.MapToPagingSpec<HistoryResource, NzbDrone.Core.History.History>("date", SortDirection.Descending);
|
||||
var pagingSpec = pagingResource.MapToPagingSpec<HistoryResource, NzbDrone.Core.History.History>(
|
||||
new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"date"
|
||||
},
|
||||
"date",
|
||||
SortDirection.Descending);
|
||||
|
||||
if (eventTypes != null && eventTypes.Any())
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Validation;
|
||||
using Prowlarr.Http;
|
||||
@@ -14,9 +15,10 @@ namespace Prowlarr.Api.V1.Indexers
|
||||
DownloadClientExistsValidator downloadClientExistsValidator)
|
||||
: base(indexerFactory, "indexer", resourceMapper, bulkResourceMapper)
|
||||
{
|
||||
Http.Validation.RuleBuilderExtensions.ValidId(SharedValidator.RuleFor(s => s.AppProfileId));
|
||||
SharedValidator.RuleFor(c => c.AppProfileId).Cascade(CascadeMode.Stop)
|
||||
.ValidId()
|
||||
.SetValidator(appProfileExistsValidator);
|
||||
|
||||
SharedValidator.RuleFor(c => c.AppProfileId).SetValidator(appProfileExistsValidator);
|
||||
SharedValidator.RuleFor(c => c.DownloadClientId).SetValidator(downloadClientExistsValidator);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
@@ -29,7 +31,11 @@ namespace Prowlarr.Api.V1.Logs
|
||||
}
|
||||
|
||||
var pagingResource = new PagingResource<LogResource>(paging);
|
||||
var pageSpec = pagingResource.MapToPagingSpec<LogResource, Log>();
|
||||
var pageSpec = pagingResource.MapToPagingSpec<LogResource, Log>(new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"id",
|
||||
"time"
|
||||
});
|
||||
|
||||
if (pageSpec.SortKey == "time")
|
||||
{
|
||||
|
||||
@@ -6373,7 +6373,7 @@
|
||||
},
|
||||
"apikey": {
|
||||
"type": "apiKey",
|
||||
"description": "Apikey passed as header",
|
||||
"description": "Apikey passed as query parameter",
|
||||
"name": "apikey",
|
||||
"in": "query"
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using Diacritical;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NzbDrone.Core.Authentication;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace Prowlarr.Http.Authentication
|
||||
{
|
||||
public static class AuthenticationBuilderExtensions
|
||||
{
|
||||
private static readonly Regex CookieNameRegex = new Regex(@"[^a-z0-9]+", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
public static AuthenticationBuilder AddApiKey(this AuthenticationBuilder authenticationBuilder, string name, Action<ApiKeyAuthenticationOptions> options)
|
||||
{
|
||||
return authenticationBuilder.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(name, options);
|
||||
@@ -29,19 +35,27 @@ namespace Prowlarr.Http.Authentication
|
||||
|
||||
public static AuthenticationBuilder AddAppAuthentication(this IServiceCollection services)
|
||||
{
|
||||
return services.AddAuthentication()
|
||||
.AddNone(AuthenticationType.None.ToString())
|
||||
.AddExternal(AuthenticationType.External.ToString())
|
||||
.AddBasic(AuthenticationType.Basic.ToString())
|
||||
.AddCookie(AuthenticationType.Forms.ToString(), options =>
|
||||
services.AddOptions<CookieAuthenticationOptions>(AuthenticationType.Forms.ToString())
|
||||
.Configure<IConfigFileProvider>((options, configFileProvider) =>
|
||||
{
|
||||
options.Cookie.Name = "ProwlarrAuth";
|
||||
// Replace diacritics and replace non-word characters to ensure cookie name doesn't contain any valid URL characters not allowed in cookie names
|
||||
var instanceName = configFileProvider.InstanceName;
|
||||
instanceName = instanceName.RemoveDiacritics();
|
||||
instanceName = CookieNameRegex.Replace(instanceName, string.Empty);
|
||||
|
||||
options.Cookie.Name = $"{instanceName}Auth";
|
||||
options.AccessDeniedPath = "/login?loginFailed=true";
|
||||
options.LoginPath = "/login";
|
||||
options.ExpireTimeSpan = TimeSpan.FromDays(7);
|
||||
options.SlidingExpiration = true;
|
||||
options.ReturnUrlParameter = "returnUrl";
|
||||
})
|
||||
});
|
||||
|
||||
return services.AddAuthentication()
|
||||
.AddNone(AuthenticationType.None.ToString())
|
||||
.AddExternal(AuthenticationType.External.ToString())
|
||||
.AddBasic(AuthenticationType.Basic.ToString())
|
||||
.AddCookie(AuthenticationType.Forms.ToString())
|
||||
.AddApiKey("API", options =>
|
||||
{
|
||||
options.HeaderName = "X-Api-Key";
|
||||
|
||||
@@ -38,7 +38,11 @@ namespace Prowlarr.Http
|
||||
|
||||
public static class PagingResourceMapper
|
||||
{
|
||||
public static PagingSpec<TModel> MapToPagingSpec<TResource, TModel>(this PagingResource<TResource> pagingResource, string defaultSortKey = "Id", SortDirection defaultSortDirection = SortDirection.Ascending)
|
||||
public static PagingSpec<TModel> MapToPagingSpec<TResource, TModel>(
|
||||
this PagingResource<TResource> pagingResource,
|
||||
HashSet<string> allowedSortKeys,
|
||||
string defaultSortKey = "id",
|
||||
SortDirection defaultSortDirection = SortDirection.Ascending)
|
||||
{
|
||||
var pagingSpec = new PagingSpec<TModel>
|
||||
{
|
||||
@@ -48,14 +52,15 @@ namespace Prowlarr.Http
|
||||
SortDirection = pagingResource.SortDirection,
|
||||
};
|
||||
|
||||
if (pagingResource.SortKey == null)
|
||||
{
|
||||
pagingSpec.SortKey = defaultSortKey;
|
||||
if (pagingResource.SortDirection == SortDirection.Default)
|
||||
{
|
||||
pagingSpec.SortDirection = defaultSortDirection;
|
||||
}
|
||||
}
|
||||
pagingSpec.SortKey = pagingResource.SortKey != null &&
|
||||
allowedSortKeys is { Count: > 0 } &&
|
||||
allowedSortKeys.Contains(pagingResource.SortKey)
|
||||
? pagingResource.SortKey
|
||||
: defaultSortKey;
|
||||
|
||||
pagingSpec.SortDirection = pagingResource.SortDirection == SortDirection.Default
|
||||
? defaultSortDirection
|
||||
: pagingResource.SortDirection;
|
||||
|
||||
return pagingSpec;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user