mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2026-04-17 21:44:48 -04:00
Compare commits
26 Commits
v1.13.3.42
...
v1.14.2.43
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
882152b911 | ||
|
|
a25e79031f | ||
|
|
cc85060b1b | ||
|
|
00bd9c241a | ||
|
|
1283e06f95 | ||
|
|
ab0108778a | ||
|
|
099b04f718 | ||
|
|
ecdc0a51a9 | ||
|
|
6c7c37affe | ||
|
|
45d378a2d9 | ||
|
|
007601cb19 | ||
|
|
5f0d6e2fdd | ||
|
|
ede9879c99 | ||
|
|
7287abc77c | ||
|
|
8c653b5c09 | ||
|
|
15c6b3c308 | ||
|
|
9676447c74 | ||
|
|
5d35f1dcc7 | ||
|
|
858f16195e | ||
|
|
a1a5dd574e | ||
|
|
a5ecc2dc9f | ||
|
|
7d46660583 | ||
|
|
22cbf40e3c | ||
|
|
25821c758f | ||
|
|
6153737a78 | ||
|
|
07adb45d63 |
2
.github/workflows/label-actions.yml
vendored
2
.github/workflows/label-actions.yml
vendored
@@ -18,6 +18,6 @@ jobs:
|
||||
action:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/label-actions@v3
|
||||
- uses: dessant/label-actions@v4
|
||||
with:
|
||||
process-only: 'issues, prs'
|
||||
|
||||
@@ -9,14 +9,14 @@ variables:
|
||||
testsFolder: './_tests'
|
||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||
majorVersion: '1.13.3'
|
||||
majorVersion: '1.14.2'
|
||||
minorVersion: $[counter('minorVersion', 1)]
|
||||
prowlarrVersion: '$(majorVersion).$(minorVersion)'
|
||||
buildName: '$(Build.SourceBranchName).$(prowlarrVersion)'
|
||||
sentryOrg: 'servarr'
|
||||
sentryUrl: 'https://sentry.servarr.com'
|
||||
dotnetVersion: '6.0.417'
|
||||
nodeVersion: '16.X'
|
||||
nodeVersion: '20.X'
|
||||
innoVersion: '6.2.2'
|
||||
windowsImage: 'windows-2022'
|
||||
linuxImage: 'ubuntu-20.04'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { maxBy } from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
@@ -50,7 +51,7 @@ class FilterBuilderModalContent extends Component {
|
||||
if (id) {
|
||||
dispatchSetFilter({ selectedFilterKey: id });
|
||||
} else {
|
||||
const last = customFilters[customFilters.length -1];
|
||||
const last = maxBy(customFilters, 'id');
|
||||
dispatchSetFilter({ selectedFilterKey: last.id });
|
||||
}
|
||||
|
||||
@@ -108,7 +109,7 @@ class FilterBuilderModalContent extends Component {
|
||||
this.setState({
|
||||
labelErrors: [
|
||||
{
|
||||
message: 'Label is required'
|
||||
message: translate('LabelIsRequired')
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -146,13 +147,13 @@ class FilterBuilderModalContent extends Component {
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
Custom Filter
|
||||
{translate('CustomFilter')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<div className={styles.labelContainer}>
|
||||
<div className={styles.label}>
|
||||
Label
|
||||
{translate('Label')}
|
||||
</div>
|
||||
|
||||
<div className={styles.labelInputContainer}>
|
||||
|
||||
@@ -37,8 +37,8 @@ class CustomFilter extends Component {
|
||||
dispatchSetFilter
|
||||
} = this.props;
|
||||
|
||||
// Assume that delete and then unmounting means the delete was successful.
|
||||
// Moving this check to a ancestor would be more accurate, but would have
|
||||
// Assume that delete and then unmounting means the deletion was successful.
|
||||
// Moving this check to an ancestor would be more accurate, but would have
|
||||
// more boilerplate.
|
||||
if (this.state.isDeleting && id === selectedFilterKey) {
|
||||
dispatchSetFilter({ selectedFilterKey: 'all' });
|
||||
|
||||
@@ -24,7 +24,8 @@ function createMapStateToProps() {
|
||||
.sort(sortByName)
|
||||
.map((downloadClient) => ({
|
||||
key: downloadClient.id,
|
||||
value: downloadClient.name
|
||||
value: downloadClient.name,
|
||||
hint: `(${downloadClient.id})`
|
||||
}));
|
||||
|
||||
if (includeAny) {
|
||||
|
||||
@@ -144,6 +144,7 @@ function EditIndexerModalContent(props) {
|
||||
}) :
|
||||
null
|
||||
}
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
|
||||
@@ -19,6 +19,7 @@ interface SavePayload {
|
||||
seedRatio?: number;
|
||||
seedTime?: number;
|
||||
packSeedTime?: number;
|
||||
rejectBlocklistedTorrentHashesWhileGrabbing?: boolean;
|
||||
}
|
||||
|
||||
interface EditIndexerModalContentProps {
|
||||
@@ -65,6 +66,10 @@ function EditIndexerModalContent(props: EditIndexerModalContentProps) {
|
||||
const [packSeedTime, setPackSeedTime] = useState<null | string | number>(
|
||||
null
|
||||
);
|
||||
const [
|
||||
rejectBlocklistedTorrentHashesWhileGrabbing,
|
||||
setRejectBlocklistedTorrentHashesWhileGrabbing,
|
||||
] = useState(NO_CHANGE);
|
||||
|
||||
const save = useCallback(() => {
|
||||
let hasChanges = false;
|
||||
@@ -105,6 +110,12 @@ function EditIndexerModalContent(props: EditIndexerModalContentProps) {
|
||||
payload.packSeedTime = packSeedTime as number;
|
||||
}
|
||||
|
||||
if (rejectBlocklistedTorrentHashesWhileGrabbing !== NO_CHANGE) {
|
||||
hasChanges = true;
|
||||
payload.rejectBlocklistedTorrentHashesWhileGrabbing =
|
||||
rejectBlocklistedTorrentHashesWhileGrabbing === 'true';
|
||||
}
|
||||
|
||||
if (hasChanges) {
|
||||
onSavePress(payload);
|
||||
}
|
||||
@@ -118,6 +129,7 @@ function EditIndexerModalContent(props: EditIndexerModalContentProps) {
|
||||
seedRatio,
|
||||
seedTime,
|
||||
packSeedTime,
|
||||
rejectBlocklistedTorrentHashesWhileGrabbing,
|
||||
onSavePress,
|
||||
onModalClose,
|
||||
]);
|
||||
@@ -146,6 +158,9 @@ function EditIndexerModalContent(props: EditIndexerModalContentProps) {
|
||||
case 'packSeedTime':
|
||||
setPackSeedTime(value);
|
||||
break;
|
||||
case 'rejectBlocklistedTorrentHashesWhileGrabbing':
|
||||
setRejectBlocklistedTorrentHashesWhileGrabbing(value);
|
||||
break;
|
||||
default:
|
||||
console.warn(`EditIndexersModalContent Unknown Input: '${name}'`);
|
||||
}
|
||||
@@ -253,6 +268,23 @@ function EditIndexerModalContent(props: EditIndexerModalContentProps) {
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup size={sizes.MEDIUM}>
|
||||
<FormLabel>
|
||||
{translate('IndexerSettingsRejectBlocklistedTorrentHashes')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="rejectBlocklistedTorrentHashesWhileGrabbing"
|
||||
value={rejectBlocklistedTorrentHashesWhileGrabbing}
|
||||
values={enableOptions}
|
||||
helpText={translate(
|
||||
'IndexerSettingsRejectBlocklistedTorrentHashesHelpText'
|
||||
)}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter className={styles.modalFooter}>
|
||||
|
||||
@@ -119,6 +119,10 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
||||
[TestCase(@"[Info] MigrationController: *** Migrating Database=prowlarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;Enlist=False ***")]
|
||||
[TestCase("/readarr/signalr/messages/negotiate?access_token=1234530f422f4aacb6b301233210aaaa&negotiateVersion=1")]
|
||||
[TestCase(@"[Info] MigrationController: *** Migrating Database=prowlarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;token=mySecret;Enlist=False&username=mySecret;mypassword=mySecret;mypass=shouldkeep1;test_token=mySecret;password=123%@%_@!#^#@;use_password=mySecret;get_token=shouldkeep2;usetoken=shouldkeep3;passwrd=mySecret;")]
|
||||
|
||||
// Discord
|
||||
[TestCase(@"https://discord.com/api/webhooks/mySecret")]
|
||||
[TestCase(@"https://discord.com/api/webhooks/mySecret/01233210")]
|
||||
public void should_clean_message(string message)
|
||||
{
|
||||
var cleansedMessage = CleanseLogMessage.Cleanse(message);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
@@ -275,6 +277,18 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
return _credentialCache.Get("credentialCache", () => new CredentialCache());
|
||||
}
|
||||
|
||||
private static bool HasRoutableIPv4Address()
|
||||
{
|
||||
// Get all IPv4 addresses from all interfaces and return true if there are any with non-loopback addresses
|
||||
var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
|
||||
|
||||
return networkInterfaces.Any(ni =>
|
||||
ni.OperationalStatus == OperationalStatus.Up &&
|
||||
ni.GetIPProperties().UnicastAddresses.Any(ip =>
|
||||
ip.Address.AddressFamily == AddressFamily.InterNetwork &&
|
||||
!IPAddress.IsLoopback(ip.Address)));
|
||||
}
|
||||
|
||||
private static async ValueTask<Stream> onConnect(SocketsHttpConnectionContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
// Until .NET supports an implementation of Happy Eyeballs (https://tools.ietf.org/html/rfc8305#section-2), let's make IPv4 fallback work in a simple way.
|
||||
@@ -298,10 +312,8 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
}
|
||||
catch
|
||||
{
|
||||
// very naively fallback to ipv4 permanently for this execution based on the response of the first connection attempt.
|
||||
// note that this may cause users to eventually get switched to ipv4 (on a random failure when they are switching networks, for instance)
|
||||
// but in the interest of keeping this implementation simple, this is acceptable.
|
||||
useIPv6 = false;
|
||||
// Do not retry IPv6 if a routable IPv4 address is available, otherwise continue to attempt IPv6 connections.
|
||||
useIPv6 = !HasRoutableIPv4Address();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -61,6 +61,9 @@ namespace NzbDrone.Common.Instrumentation
|
||||
|
||||
// Applications
|
||||
new (@"""name"":""apikey"",""value"":""(?<secret>[^&=]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// Discord
|
||||
new (@"discord.com/api/webhooks/((?<secret>[\w-]+)/)?(?<secret>[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase)
|
||||
};
|
||||
|
||||
private static readonly Regex CleanseRemoteIPRegex = new (@"(?:Auth-\w+(?<!Failure|Unauthorized) ip|from) (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", RegexOptions.Compiled);
|
||||
|
||||
@@ -218,7 +218,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
{
|
||||
var cacheKey = $"{Settings.BaseUrl}";
|
||||
var schemas = _schemaCache.Get(cacheKey, () => _lidarrV1Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7));
|
||||
var syncFields = new List<string> { "baseUrl", "apiPath", "apiKey", "categories", "minimumSeeders", "seedCriteria.seedRatio", "seedCriteria.seedTime", "seedCriteria.discographySeedTime" };
|
||||
var syncFields = new List<string> { "baseUrl", "apiPath", "apiKey", "categories", "minimumSeeders", "seedCriteria.seedRatio", "seedCriteria.seedTime", "seedCriteria.discographySeedTime", "rejectBlocklistedTorrentHashesWhileGrabbing" };
|
||||
|
||||
if (id == 0)
|
||||
{
|
||||
@@ -258,10 +258,15 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.SeedRatio;
|
||||
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedTime").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.SeedTime;
|
||||
|
||||
if (lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "seedCriteria.discographySeedTime") != null)
|
||||
if (lidarrIndexer.Fields.Any(x => x.Name == "seedCriteria.discographySeedTime"))
|
||||
{
|
||||
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "seedCriteria.discographySeedTime").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.PackSeedTime ?? ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.SeedTime;
|
||||
}
|
||||
|
||||
if (lidarrIndexer.Fields.Any(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing"))
|
||||
{
|
||||
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.RejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
}
|
||||
}
|
||||
|
||||
return lidarrIndexer;
|
||||
|
||||
@@ -55,6 +55,10 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
var otherSeedRatio = other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio")?.Value == null ? null : (double?)Convert.ToDouble(other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio").Value);
|
||||
var seedRatioCompare = seedRatio == otherSeedRatio;
|
||||
|
||||
var rejectBlocklistedTorrentHashesWhileGrabbing = Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing")?.Value == null ? null : (bool?)Convert.ToBoolean(Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value);
|
||||
var otherRejectBlocklistedTorrentHashesWhileGrabbing = other.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing")?.Value == null ? null : (bool?)Convert.ToBoolean(other.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value);
|
||||
var rejectBlocklistedTorrentHashesWhileGrabbingCompare = rejectBlocklistedTorrentHashesWhileGrabbing == otherRejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
|
||||
return other.EnableRss == EnableRss &&
|
||||
other.EnableAutomaticSearch == EnableAutomaticSearch &&
|
||||
other.EnableInteractiveSearch == EnableInteractiveSearch &&
|
||||
@@ -62,7 +66,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
other.Implementation == Implementation &&
|
||||
other.Priority == Priority &&
|
||||
other.Id == Id &&
|
||||
apiKeyCompare && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && discographySeedTimeCompare;
|
||||
apiKeyCompare && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && discographySeedTimeCompare && rejectBlocklistedTorrentHashesWhileGrabbingCompare;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
{
|
||||
var cacheKey = $"{Settings.BaseUrl}";
|
||||
var schemas = _schemaCache.Get(cacheKey, () => _radarrV3Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7));
|
||||
var syncFields = new List<string> { "baseUrl", "apiPath", "apiKey", "categories", "minimumSeeders", "seedCriteria.seedRatio", "seedCriteria.seedTime" };
|
||||
var syncFields = new List<string> { "baseUrl", "apiPath", "apiKey", "categories", "minimumSeeders", "seedCriteria.seedRatio", "seedCriteria.seedTime", "rejectBlocklistedTorrentHashesWhileGrabbing" };
|
||||
|
||||
if (id == 0)
|
||||
{
|
||||
@@ -255,6 +255,11 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "minimumSeeders").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.AppMinimumSeeders ?? indexer.AppProfile.Value.MinimumSeeders;
|
||||
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.SeedRatio;
|
||||
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedTime").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.SeedTime;
|
||||
|
||||
if (radarrIndexer.Fields.Any(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing"))
|
||||
{
|
||||
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.RejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
}
|
||||
}
|
||||
|
||||
return radarrIndexer;
|
||||
|
||||
@@ -51,6 +51,10 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
var otherSeedRatio = other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio")?.Value == null ? null : (double?)Convert.ToDouble(other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio").Value);
|
||||
var seedRatioCompare = seedRatio == otherSeedRatio;
|
||||
|
||||
var rejectBlocklistedTorrentHashesWhileGrabbing = Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing")?.Value == null ? null : (bool?)Convert.ToBoolean(Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value);
|
||||
var otherRejectBlocklistedTorrentHashesWhileGrabbing = other.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing")?.Value == null ? null : (bool?)Convert.ToBoolean(other.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value);
|
||||
var rejectBlocklistedTorrentHashesWhileGrabbingCompare = rejectBlocklistedTorrentHashesWhileGrabbing == otherRejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
|
||||
return other.EnableRss == EnableRss &&
|
||||
other.EnableAutomaticSearch == EnableAutomaticSearch &&
|
||||
other.EnableInteractiveSearch == EnableInteractiveSearch &&
|
||||
@@ -58,7 +62,7 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
other.Implementation == Implementation &&
|
||||
other.Priority == Priority &&
|
||||
other.Id == Id &&
|
||||
apiKeyCompare && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare;
|
||||
apiKeyCompare && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && rejectBlocklistedTorrentHashesWhileGrabbingCompare;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
{
|
||||
var cacheKey = $"{Settings.BaseUrl}";
|
||||
var schemas = _schemaCache.Get(cacheKey, () => _readarrV1Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7));
|
||||
var syncFields = new[] { "baseUrl", "apiPath", "apiKey", "categories", "minimumSeeders", "seedCriteria.seedRatio", "seedCriteria.seedTime", "seedCriteria.discographySeedTime" };
|
||||
var syncFields = new[] { "baseUrl", "apiPath", "apiKey", "categories", "minimumSeeders", "seedCriteria.seedRatio", "seedCriteria.seedTime", "seedCriteria.discographySeedTime", "rejectBlocklistedTorrentHashesWhileGrabbing" };
|
||||
|
||||
var newznab = schemas.First(i => i.Implementation == "Newznab");
|
||||
var torznab = schemas.First(i => i.Implementation == "Torznab");
|
||||
@@ -252,10 +252,15 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.SeedRatio;
|
||||
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedTime").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.SeedTime;
|
||||
|
||||
if (readarrIndexer.Fields.FirstOrDefault(x => x.Name == "seedCriteria.discographySeedTime") != null)
|
||||
if (readarrIndexer.Fields.Any(x => x.Name == "seedCriteria.discographySeedTime"))
|
||||
{
|
||||
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "seedCriteria.discographySeedTime").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.PackSeedTime ?? ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.SeedTime;
|
||||
}
|
||||
|
||||
if (readarrIndexer.Fields.Any(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing"))
|
||||
{
|
||||
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.RejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
}
|
||||
}
|
||||
|
||||
return readarrIndexer;
|
||||
|
||||
@@ -55,6 +55,10 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
var otherSeedRatio = other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio")?.Value == null ? null : (double?)Convert.ToDouble(other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio").Value);
|
||||
var seedRatioCompare = seedRatio == otherSeedRatio;
|
||||
|
||||
var rejectBlocklistedTorrentHashesWhileGrabbing = Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing")?.Value == null ? null : (bool?)Convert.ToBoolean(Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value);
|
||||
var otherRejectBlocklistedTorrentHashesWhileGrabbing = other.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing")?.Value == null ? null : (bool?)Convert.ToBoolean(other.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value);
|
||||
var rejectBlocklistedTorrentHashesWhileGrabbingCompare = rejectBlocklistedTorrentHashesWhileGrabbing == otherRejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
|
||||
return other.EnableRss == EnableRss &&
|
||||
other.EnableAutomaticSearch == EnableAutomaticSearch &&
|
||||
other.EnableInteractiveSearch == EnableInteractiveSearch &&
|
||||
@@ -62,7 +66,7 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
other.Implementation == Implementation &&
|
||||
other.Priority == Priority &&
|
||||
other.Id == Id &&
|
||||
apiKeyCompare && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && discographySeedTimeCompare;
|
||||
apiKeyCompare && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && discographySeedTimeCompare && rejectBlocklistedTorrentHashesWhileGrabbingCompare;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
{
|
||||
var cacheKey = $"{Settings.BaseUrl}";
|
||||
var schemas = _schemaCache.Get(cacheKey, () => _sonarrV3Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7));
|
||||
var syncFields = new List<string> { "baseUrl", "apiPath", "apiKey", "categories", "animeCategories", "animeStandardFormatSearch", "minimumSeeders", "seedCriteria.seedRatio", "seedCriteria.seedTime", "seedCriteria.seasonPackSeedTime" };
|
||||
var syncFields = new List<string> { "baseUrl", "apiPath", "apiKey", "categories", "animeCategories", "animeStandardFormatSearch", "minimumSeeders", "seedCriteria.seedRatio", "seedCriteria.seedTime", "seedCriteria.seasonPackSeedTime", "rejectBlocklistedTorrentHashesWhileGrabbing" };
|
||||
|
||||
if (id == 0)
|
||||
{
|
||||
@@ -270,6 +270,11 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.SeedRatio;
|
||||
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedTime").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.SeedTime;
|
||||
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seasonPackSeedTime").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.PackSeedTime ?? ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.SeedTime;
|
||||
|
||||
if (sonarrIndexer.Fields.Any(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing"))
|
||||
{
|
||||
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.RejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
}
|
||||
}
|
||||
|
||||
return sonarrIndexer;
|
||||
|
||||
@@ -61,6 +61,10 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
var otherSeedRatio = other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio")?.Value == null ? null : (double?)Convert.ToDouble(other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio").Value);
|
||||
var seedRatioCompare = seedRatio == otherSeedRatio;
|
||||
|
||||
var rejectBlocklistedTorrentHashesWhileGrabbing = Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing")?.Value == null ? null : (bool?)Convert.ToBoolean(Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value);
|
||||
var otherRejectBlocklistedTorrentHashesWhileGrabbing = other.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing")?.Value == null ? null : (bool?)Convert.ToBoolean(other.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value);
|
||||
var rejectBlocklistedTorrentHashesWhileGrabbingCompare = rejectBlocklistedTorrentHashesWhileGrabbing == otherRejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
|
||||
return other.EnableRss == EnableRss &&
|
||||
other.EnableAutomaticSearch == EnableAutomaticSearch &&
|
||||
other.EnableInteractiveSearch == EnableInteractiveSearch &&
|
||||
@@ -68,7 +72,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
other.Implementation == Implementation &&
|
||||
other.Priority == Priority &&
|
||||
other.Id == Id &&
|
||||
apiKeyCompare && apiPathCompare && baseUrl && cats && animeCats && animeStandardFormatSearchCompare && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && seasonSeedTimeCompare;
|
||||
apiKeyCompare && apiPathCompare && baseUrl && cats && animeCats && animeStandardFormatSearchCompare && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && seasonSeedTimeCompare && rejectBlocklistedTorrentHashesWhileGrabbingCompare;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
{
|
||||
var cacheKey = $"{Settings.BaseUrl}";
|
||||
var schemas = _schemaCache.Get(cacheKey, () => _whisparrV3Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7));
|
||||
var syncFields = new[] { "baseUrl", "apiPath", "apiKey", "categories", "minimumSeeders", "seedCriteria.seedRatio", "seedCriteria.seedTime" };
|
||||
var syncFields = new[] { "baseUrl", "apiPath", "apiKey", "categories", "minimumSeeders", "seedCriteria.seedRatio", "seedCriteria.seedTime", "seedCriteria.seasonPackSeedTime", "rejectBlocklistedTorrentHashesWhileGrabbing" };
|
||||
|
||||
var newznab = schemas.First(i => i.Implementation == "Newznab");
|
||||
var torznab = schemas.First(i => i.Implementation == "Torznab");
|
||||
@@ -251,6 +251,16 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
whisparrIndexer.Fields.FirstOrDefault(x => x.Name == "minimumSeeders").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.AppMinimumSeeders ?? indexer.AppProfile.Value.MinimumSeeders;
|
||||
whisparrIndexer.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.SeedRatio;
|
||||
whisparrIndexer.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedTime").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.SeedTime;
|
||||
|
||||
if (whisparrIndexer.Fields.Any(x => x.Name == "seedCriteria.seasonPackSeedTime"))
|
||||
{
|
||||
whisparrIndexer.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seasonPackSeedTime").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.PackSeedTime ?? ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.SeedTime;
|
||||
}
|
||||
|
||||
if (whisparrIndexer.Fields.Any(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing"))
|
||||
{
|
||||
whisparrIndexer.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value = ((ITorrentIndexerSettings)indexer.Settings).TorrentBaseSettings.RejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
}
|
||||
}
|
||||
|
||||
return whisparrIndexer;
|
||||
|
||||
@@ -47,10 +47,18 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
var otherSeedTime = other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedTime")?.Value == null ? null : (int?)Convert.ToInt32(other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedTime").Value);
|
||||
var seedTimeCompare = seedTime == otherSeedTime;
|
||||
|
||||
var seasonSeedTime = Fields.FirstOrDefault(x => x.Name == "seedCriteria.seasonPackSeedTime")?.Value == null ? null : (int?)Convert.ToInt32(Fields.FirstOrDefault(x => x.Name == "seedCriteria.seasonPackSeedTime").Value);
|
||||
var otherSeasonSeedTime = other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seasonPackSeedTime")?.Value == null ? null : (int?)Convert.ToInt32(other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seasonPackSeedTime").Value);
|
||||
var seasonSeedTimeCompare = seasonSeedTime == otherSeasonSeedTime;
|
||||
|
||||
var seedRatio = Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio")?.Value == null ? null : (double?)Convert.ToDouble(Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio").Value);
|
||||
var otherSeedRatio = other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio")?.Value == null ? null : (double?)Convert.ToDouble(other.Fields.FirstOrDefault(x => x.Name == "seedCriteria.seedRatio").Value);
|
||||
var seedRatioCompare = seedRatio == otherSeedRatio;
|
||||
|
||||
var rejectBlocklistedTorrentHashesWhileGrabbing = Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing")?.Value == null ? null : (bool?)Convert.ToBoolean(Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value);
|
||||
var otherRejectBlocklistedTorrentHashesWhileGrabbing = other.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing")?.Value == null ? null : (bool?)Convert.ToBoolean(other.Fields.FirstOrDefault(x => x.Name == "rejectBlocklistedTorrentHashesWhileGrabbing").Value);
|
||||
var rejectBlocklistedTorrentHashesWhileGrabbingCompare = rejectBlocklistedTorrentHashesWhileGrabbing == otherRejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
|
||||
return other.EnableRss == EnableRss &&
|
||||
other.EnableAutomaticSearch == EnableAutomaticSearch &&
|
||||
other.EnableInteractiveSearch == EnableInteractiveSearch &&
|
||||
@@ -58,7 +66,7 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
other.Implementation == Implementation &&
|
||||
other.Priority == Priority &&
|
||||
other.Id == Id &&
|
||||
apiKeyCompare && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare;
|
||||
apiKeyCompare && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && seasonSeedTimeCompare && rejectBlocklistedTorrentHashesWhileGrabbingCompare;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,13 +32,13 @@ namespace NzbDrone.Core.Download.Clients.Aria2
|
||||
[FieldDefinition(1, Label = "Port", Type = FieldType.Number)]
|
||||
public int Port { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "XML RPC Path", Type = FieldType.Textbox)]
|
||||
[FieldDefinition(2, Label = "XmlRpcPath", Type = FieldType.Textbox)]
|
||||
public string RpcPath { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Use SSL", Type = FieldType.Checkbox)]
|
||||
[FieldDefinition(3, Label = "UseSsl", Type = FieldType.Checkbox)]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Secret token", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
[FieldDefinition(4, Label = "SecretToken", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string SecretToken { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Directory", Type = FieldType.Textbox, HelpText = "DownloadClientAriaSettingsDirectoryHelpText")]
|
||||
|
||||
@@ -27,15 +27,16 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||
|
||||
private static readonly TorrentBlackholeSettingsValidator Validator = new TorrentBlackholeSettingsValidator();
|
||||
|
||||
[FieldDefinition(0, Label = "Torrent Folder", Type = FieldType.Path, HelpText = "Folder in which Prowlarr will store the .torrent file")]
|
||||
[FieldDefinition(0, Label = "TorrentBlackholeTorrentFolder", Type = FieldType.Path, HelpText = "BlackholeFolderHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "TorrentBlackholeTorrentFolder", "extension", ".torrent")]
|
||||
public string TorrentFolder { get; set; }
|
||||
|
||||
[DefaultValue(false)]
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||
[FieldDefinition(1, Label = "Save Magnet Files", Type = FieldType.Checkbox, HelpText = "Save a .magnet file with the magnet link if no .torrent file is available (only useful if the download client supports .magnet files)")]
|
||||
[FieldDefinition(1, Label = "TorrentBlackholeSaveMagnetFiles", Type = FieldType.Checkbox, HelpText = "TorrentBlackholeSaveMagnetFilesHelpText")]
|
||||
public bool SaveMagnetFiles { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Save Magnet Files", Type = FieldType.Textbox, HelpText = "Extension to use for magnet links, defaults to '.magnet'")]
|
||||
[FieldDefinition(2, Label = "TorrentBlackholeSaveMagnetFilesExtension", Type = FieldType.Textbox, HelpText = "TorrentBlackholeSaveMagnetFilesExtensionHelpText")]
|
||||
public string MagnetFileExtension { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -18,7 +18,8 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||
{
|
||||
private static readonly UsenetBlackholeSettingsValidator Validator = new UsenetBlackholeSettingsValidator();
|
||||
|
||||
[FieldDefinition(0, Label = "Nzb Folder", Type = FieldType.Path, HelpText = "Folder in which Prowlarr will store the .nzb file")]
|
||||
[FieldDefinition(0, Label = "UsenetBlackholeNzbFolder", Type = FieldType.Path, HelpText = "BlackholeFolderHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UsenetBlackholeNzbFolder", "extension", ".nzb")]
|
||||
public string NzbFolder { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -34,22 +34,24 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
|
||||
public int Port { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Deluge")]
|
||||
[FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Deluge")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the deluge json url, see http://[host]:[port]/[urlBase]/json")]
|
||||
[FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientDelugeSettingsUrlBaseHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/json")]
|
||||
public string UrlBase { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Default Category", Type = FieldType.Textbox, HelpText = "Default fallback Category if no mapped category exists for a release. Adding a category specific to Prowlarr avoids conflicts with unrelated downloads, but it's optional")]
|
||||
[FieldDefinition(5, Label = "DefaultCategory", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsDefaultCategoryHelpText")]
|
||||
public string Category { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "Priority to use when grabbing items")]
|
||||
[FieldDefinition(6, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "DownloadClientSettingsPriorityItemHelpText")]
|
||||
public int Priority { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "Add Paused", Type = FieldType.Checkbox)]
|
||||
[FieldDefinition(7, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox)]
|
||||
public bool AddPaused { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -36,7 +36,8 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
|
||||
public int Port { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Download Station")]
|
||||
[FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Download Station")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
@@ -45,10 +46,10 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||
[FieldDefinition(4, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Default Category", Type = FieldType.Textbox, HelpText = "Default fallback category if no mapped category exists for a release. Adding a category specific to Prowlarr avoids conflicts with unrelated downloads, but it's optional. Creates a [category] subdirectory in the output directory.")]
|
||||
[FieldDefinition(5, Label = "DefaultCategory", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsDefaultCategoryHelpText")]
|
||||
public string Category { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Directory", Type = FieldType.Textbox, HelpText = "Optional shared folder to put downloads into, leave blank to use the default Download Station location")]
|
||||
[FieldDefinition(6, Label = "Directory", Type = FieldType.Textbox, HelpText = "DownloadClientDownloadStationSettingsDirectoryHelpText")]
|
||||
public string TvDirectory { get; set; }
|
||||
|
||||
public DownloadStationSettings()
|
||||
|
||||
@@ -40,10 +40,12 @@ namespace NzbDrone.Core.Download.Clients.Flood
|
||||
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
|
||||
public int Port { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Flood")]
|
||||
[FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Flood")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, HelpText = "Optionally adds a prefix to Flood API, such as [protocol]://[host]:[port]/[urlBase]api")]
|
||||
[FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, HelpText = "DownloadClientFloodSettingsUrlBaseHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "url", "[protocol]://[host]:[port]/[urlBase]/api")]
|
||||
public string UrlBase { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
@@ -52,16 +54,16 @@ namespace NzbDrone.Core.Download.Clients.Flood
|
||||
[FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Destination", Type = FieldType.Textbox, HelpText = "Manually specifies download destination")]
|
||||
[FieldDefinition(6, Label = "Destination", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsDestinationHelpText")]
|
||||
public string Destination { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "Tags", Type = FieldType.Tag, HelpText = "Initial tags of a download. To be recognized, a download must have all initial tags. This avoids conflicts with unrelated downloads.")]
|
||||
[FieldDefinition(7, Label = "Tags", Type = FieldType.Tag, HelpText = "DownloadClientFloodSettingsTagsHelpText")]
|
||||
public IEnumerable<string> Tags { get; set; }
|
||||
|
||||
[FieldDefinition(8, Label = "Additional Tags", Type = FieldType.Select, SelectOptions = typeof(AdditionalTags), HelpText = "Adds properties of media as tags. Hints are examples.", Advanced = true)]
|
||||
[FieldDefinition(8, Label = "DownloadClientFloodSettingsAdditionalTags", Type = FieldType.Select, SelectOptions = typeof(AdditionalTags), HelpText = "DownloadClientFloodSettingsAdditionalTagsHelpText", Advanced = true)]
|
||||
public IEnumerable<int> AdditionalTags { get; set; }
|
||||
|
||||
[FieldDefinition(9, Label = "Add Paused", Type = FieldType.Checkbox)]
|
||||
[FieldDefinition(9, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox)]
|
||||
public bool AddPaused { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -46,34 +46,39 @@ namespace NzbDrone.Core.Download.Clients.FreeboxDownload
|
||||
ApiUrl = "/api/v1/";
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox, HelpText = "Hostname or host IP address of the Freebox, defaults to 'mafreebox.freebox.fr' (will only work if on same network)")]
|
||||
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox, HelpText = "DownloadClientFreeboxSettingsHostHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "Host", "url", "mafreebox.freebox.fr")]
|
||||
public string Host { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox, HelpText = "Port used to access Freebox interface, defaults to '443'")]
|
||||
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox, HelpText = "DownloadClientFreeboxSettingsPortHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "Port", "port", 443)]
|
||||
public int Port { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secured connection when connecting to Freebox API")]
|
||||
[FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Freebox API")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "API URL", Type = FieldType.Textbox, Advanced = true, HelpText = "Define Freebox API base URL with API version, eg http://[host]:[port]/[api_base_url]/[api_version]/, defaults to '/api/v1/'")]
|
||||
[FieldDefinition(3, Label = "DownloadClientFreeboxSettingsApiUrl", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientFreeboxSettingsApiUrlHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "DownloadClientFreeboxSettingsApiUrl", "url", "http://[host]:[port]/[api_base_url]/[api_version]/")]
|
||||
[FieldToken(TokenField.HelpText, "DownloadClientFreeboxSettingsApiUrl", "defaultApiUrl", "/api/v1/")]
|
||||
public string ApiUrl { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "App ID", Type = FieldType.Textbox, HelpText = "App ID given when creating access to Freebox API (ie 'app_id')")]
|
||||
[FieldDefinition(4, Label = "DownloadClientFreeboxSettingsAppId", Type = FieldType.Textbox, HelpText = "DownloadClientFreeboxSettingsAppIdHelpText")]
|
||||
public string AppId { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "App Token", Type = FieldType.Password, Privacy = PrivacyLevel.Password, HelpText = "App token retrieved when creating access to Freebox API (ie 'app_token')")]
|
||||
[FieldDefinition(5, Label = "DownloadClientFreeboxSettingsAppToken", Type = FieldType.Password, Privacy = PrivacyLevel.Password, HelpText = "DownloadClientFreeboxSettingsAppTokenHelpText")]
|
||||
public string AppToken { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Destination Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default Freebox download location")]
|
||||
[FieldDefinition(6, Label = "Destination", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsDestinationHelpText")]
|
||||
public string DestinationDirectory { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "Default Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Prowlarr avoids conflicts with unrelated non-Prowlarr downloads (will create a [category] subdirectory in the output directory)")]
|
||||
[FieldDefinition(7, Label = "DefaultCategory", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsDefaultCategoryHelpText")]
|
||||
public string Category { get; set; }
|
||||
|
||||
[FieldDefinition(8, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(FreeboxDownloadPriority), HelpText = "Priority to use when grabbing")]
|
||||
[FieldDefinition(8, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(FreeboxDownloadPriority), HelpText = "DownloadClientSettingsPriorityItemHelpText")]
|
||||
public int Priority { get; set; }
|
||||
|
||||
[FieldDefinition(9, Label = "Add Paused", Type = FieldType.Checkbox)]
|
||||
[FieldDefinition(9, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox)]
|
||||
public bool AddPaused { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -39,10 +39,13 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
|
||||
public int Port { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Hadouken")]
|
||||
[FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Hadouken")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the Hadouken url, e.g. http://[host]:[port]/[urlBase]/api")]
|
||||
[FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsUrlBaseHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "clientName", "Hadouken")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/api")]
|
||||
public string UrlBase { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
@@ -51,7 +54,7 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||
[FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Default Category", Type = FieldType.Textbox, HelpText = "Default fallback category if no mapped category exists for a release.")]
|
||||
[FieldDefinition(6, Label = "DefaultCategory", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsDefaultCategoryHelpText")]
|
||||
public string Category { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -41,16 +41,18 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
|
||||
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
|
||||
public int Port { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the NZBVortex url, e.g. http://[host]:[port]/[urlBase]/api")]
|
||||
[FieldDefinition(2, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsUrlBaseHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "clientName", "NZBVortex")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/api")]
|
||||
public string UrlBase { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "API Key", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey)]
|
||||
[FieldDefinition(3, Label = "ApiKey", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey)]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Default Category", Type = FieldType.Textbox, HelpText = "Default fallback category if no mapped category exists for a release. Adding a category specific to Prowlarr avoids conflicts with unrelated downloads, but it's optional")]
|
||||
[FieldDefinition(4, Label = "DefaultCategory", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsDefaultCategoryHelpText")]
|
||||
public string Category { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "Priority to use when grabbing items")]
|
||||
[FieldDefinition(5, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "DownloadClientSettingsPriorityItemHelpText")]
|
||||
public int Priority { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -41,10 +41,13 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
|
||||
public int Port { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to NZBGet")]
|
||||
[FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "NZBGet")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the NZBGet url, e.g. http://[host]:[port]/[urlBase]/jsonrpc")]
|
||||
[FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsUrlBaseHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "clientName", "NZBGet")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/jsonrpc")]
|
||||
public string UrlBase { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
@@ -53,13 +56,13 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||
[FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Default Category", Type = FieldType.Textbox, HelpText = "Default fallback category if no mapped category exists for a release. Adding a category specific to Prowlarr avoids conflicts with unrelated downloads, but it's optional")]
|
||||
[FieldDefinition(6, Label = "DefaultCategory", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsDefaultCategoryHelpText")]
|
||||
public string Category { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority for items added from Prowlarr")]
|
||||
[FieldDefinition(7, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "DownloadClientSettingsPriorityItemHelpText")]
|
||||
public int Priority { get; set; }
|
||||
|
||||
[FieldDefinition(8, Label = "Add Paused", Type = FieldType.Checkbox, HelpText = "This option requires at least NZBGet version 16.0")]
|
||||
[FieldDefinition(8, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox, HelpText = "DownloadClientNzbgetSettingsAddPausedHelpText")]
|
||||
public bool AddPaused { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -19,10 +19,10 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
|
||||
{
|
||||
private static readonly PneumaticSettingsValidator Validator = new PneumaticSettingsValidator();
|
||||
|
||||
[FieldDefinition(0, Label = "Nzb Folder", Type = FieldType.Path, HelpText = "This folder will need to be reachable from XBMC")]
|
||||
[FieldDefinition(0, Label = "DownloadClientPneumaticSettingsNzbFolder", Type = FieldType.Path, HelpText = "DownloadClientPneumaticSettingsNzbFolderHelpText")]
|
||||
public string NzbFolder { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Strm Folder", Type = FieldType.Path, HelpText = ".strm files in this folder will be import by drone")]
|
||||
[FieldDefinition(1, Label = "DownloadClientPneumaticSettingsStrmFolder", Type = FieldType.Path, HelpText = "DownloadClientPneumaticSettingsStrmFolderHelpText")]
|
||||
public string StrmFolder { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -35,10 +35,12 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
|
||||
public int Port { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use a secure connection. See Options -> Web UI -> 'Use HTTPS instead of HTTP' in qBittorrent.")]
|
||||
[FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientQbittorrentSettingsUseSslHelpText")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the qBittorrent url, e.g. http://[host]:[port]/[urlBase]/api")]
|
||||
[FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsUrlBaseHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "clientName", "qBittorrent")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/api")]
|
||||
public string UrlBase { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
@@ -47,22 +49,22 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
[FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Default Category", Type = FieldType.Textbox, HelpText = "Default fallback category if no mapped category exists for a release. Adding a category specific to Prowlarr avoids conflicts with unrelated downloads, but it's optional")]
|
||||
[FieldDefinition(6, Label = "DefaultCategory", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsDefaultCategoryHelpText")]
|
||||
public string Category { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "Priority to use when grabbing items")]
|
||||
[FieldDefinition(7, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "DownloadClientSettingsPriorityItemHelpText")]
|
||||
public int Priority { get; set; }
|
||||
|
||||
[FieldDefinition(8, Label = "Initial State", Type = FieldType.Select, SelectOptions = typeof(QBittorrentState), HelpText = "Initial state for torrents added to qBittorrent")]
|
||||
[FieldDefinition(8, Label = "DownloadClientSettingsInitialState", Type = FieldType.Select, SelectOptions = typeof(QBittorrentState), HelpText = "DownloadClientQbittorrentSettingsInitialStateHelpText")]
|
||||
public int InitialState { get; set; }
|
||||
|
||||
[FieldDefinition(9, Label = "Sequential Order", Type = FieldType.Checkbox, HelpText = "Download in sequential order (qBittorrent 4.1.0+)")]
|
||||
[FieldDefinition(9, Label = "DownloadClientQbittorrentSettingsSequentialOrder", Type = FieldType.Checkbox, HelpText = "DownloadClientQbittorrentSettingsSequentialOrderHelpText")]
|
||||
public bool SequentialOrder { get; set; }
|
||||
|
||||
[FieldDefinition(10, Label = "First and Last First", Type = FieldType.Checkbox, HelpText = "Download first and last pieces first (qBittorrent 4.1.0+)")]
|
||||
[FieldDefinition(10, Label = "DownloadClientQbittorrentSettingsFirstAndLastFirst", Type = FieldType.Checkbox, HelpText = "DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText")]
|
||||
public bool FirstAndLast { get; set; }
|
||||
|
||||
[FieldDefinition(13, Label = "DownloadClientQbittorrentSettingsContentLayout", Type = FieldType.Select, SelectOptions = typeof(QBittorrentContentLayout), HelpText = "DownloadClientQbittorrentSettingsContentLayoutHelpText")]
|
||||
[FieldDefinition(11, Label = "DownloadClientQbittorrentSettingsContentLayout", Type = FieldType.Select, SelectOptions = typeof(QBittorrentContentLayout), HelpText = "DownloadClientQbittorrentSettingsContentLayoutHelpText")]
|
||||
public int ContentLayout { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -50,13 +50,16 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
|
||||
public int Port { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Sabnzbd")]
|
||||
[FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Sabnzbd")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the Sabnzbd url, e.g. http://[host]:[port]/[urlBase]/api")]
|
||||
[FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsUrlBaseHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "clientName", "Sabnzbd")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/api")]
|
||||
public string UrlBase { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "API Key", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey)]
|
||||
[FieldDefinition(4, Label = "ApiKey", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey)]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
@@ -65,10 +68,10 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
[FieldDefinition(6, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "Default Category", Type = FieldType.Textbox, HelpText = "Default fallback category if no mapped category exists for a release. Adding a category specific to Prowlarr avoids conflicts with unrelated downloads, but it's optional")]
|
||||
[FieldDefinition(7, Label = "DefaultCategory", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsDefaultCategoryHelpText")]
|
||||
public string Category { get; set; }
|
||||
|
||||
[FieldDefinition(8, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing items")]
|
||||
[FieldDefinition(8, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "DownloadClientSettingsPriorityItemHelpText")]
|
||||
public int Priority { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -41,10 +41,14 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
|
||||
public int Port { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Transmission")]
|
||||
[FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Transmission")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the transmission rpc url, eg http://[host]:[port]/[urlBase]/rpc, defaults to '/transmission/'")]
|
||||
[FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientTransmissionSettingsUrlBaseHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "clientName", "Transmission")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/rpc")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "defaultUrl", "/transmission/")]
|
||||
public string UrlBase { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
@@ -53,16 +57,16 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
[FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Default Category", Type = FieldType.Textbox, HelpText = "Default fallback category if no mapped category exists for a release. Adding a category specific to Prowlarr avoids conflicts with unrelated downloads, but it's optional. Creates a [category] subdirectory in the output directory.")]
|
||||
[FieldDefinition(6, Label = "DefaultCategory", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsDefaultCategorySubFolderHelpText")]
|
||||
public string Category { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default Transmission location")]
|
||||
[FieldDefinition(7, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientTransmissionSettingsDirectoryHelpText")]
|
||||
public string Directory { get; set; }
|
||||
|
||||
[FieldDefinition(8, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "Priority to use when grabbing items")]
|
||||
[FieldDefinition(8, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "DownloadClientSettingsPriorityItemHelpText")]
|
||||
public int Priority { get; set; }
|
||||
|
||||
[FieldDefinition(9, Label = "Add Paused", Type = FieldType.Checkbox)]
|
||||
[FieldDefinition(9, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox)]
|
||||
public bool AddPaused { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -36,10 +36,13 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
|
||||
public int Port { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to ruTorrent")]
|
||||
[FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "rTorrent")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Url Path", Type = FieldType.Textbox, HelpText = "Path to the XMLRPC endpoint, see http(s)://[host]:[port]/[urlPath]. When using ruTorrent this usually is RPC2 or (path to ruTorrent)/plugins/rpc/rpc.php")]
|
||||
[FieldDefinition(3, Label = "DownloadClientRTorrentSettingsUrlPath", Type = FieldType.Textbox, HelpText = "DownloadClientRTorrentSettingsUrlPathHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "DownloadClientRTorrentSettingsUrlPath", "url", "http(s)://[host]:[port]/[urlPath]")]
|
||||
[FieldToken(TokenField.HelpText, "DownloadClientRTorrentSettingsUrlPath", "url2", "/plugins/rpc/rpc.php")]
|
||||
public string UrlBase { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
@@ -48,16 +51,16 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||
[FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Default Category", Type = FieldType.Textbox, HelpText = "Default fallback category if no mapped category exists for a release. Adding a category specific to Prowlarr avoids conflicts with unrelated downloads, but it's optional.")]
|
||||
[FieldDefinition(6, Label = "DefaultCategory", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsDefaultCategoryHelpText")]
|
||||
public string Category { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default rTorrent location")]
|
||||
[FieldDefinition(7, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientRTorrentSettingsDirectoryHelpText")]
|
||||
public string Directory { get; set; }
|
||||
|
||||
[FieldDefinition(8, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing items")]
|
||||
[FieldDefinition(8, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "DownloadClientSettingsPriorityItemHelpText")]
|
||||
public int Priority { get; set; }
|
||||
|
||||
[FieldDefinition(9, Label = "Add Stopped", Type = FieldType.Checkbox, HelpText = "Enabling will add torrents and magnets to ruTorrent in a stopped state")]
|
||||
[FieldDefinition(9, Label = "DownloadClientRTorrentSettingsAddStopped", Type = FieldType.Checkbox, HelpText = "DownloadClientRTorrentSettingsAddStoppedHelpText")]
|
||||
public bool AddStopped { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -34,10 +34,13 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
|
||||
public int Port { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to uTorrent")]
|
||||
[FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UseSsl", "clientName", "uTorrent")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the uTorrent url, e.g. http://[host]:[port]/[urlBase]/api")]
|
||||
[FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsUrlBaseHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "clientName", "uTorrent")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/api")]
|
||||
public string UrlBase { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
@@ -46,13 +49,14 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||
[FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Default Category", Type = FieldType.Textbox, HelpText = "Default fallback category if no mapped category exists for a release. Adding a category specific to Prowlarr avoids conflicts with unrelated downloads, but it's optional")]
|
||||
[FieldDefinition(6, Label = "DefaultCategory", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsDefaultCategoryHelpText")]
|
||||
public string Category { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "Priority to use when grabbing items")]
|
||||
[FieldDefinition(7, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "DownloadClientSettingsPriorityItemHelpText")]
|
||||
public int Priority { get; set; }
|
||||
|
||||
[FieldDefinition(8, Label = "Initial State", Type = FieldType.Select, SelectOptions = typeof(UTorrentState), HelpText = "Initial state for torrents added to uTorrent")]
|
||||
[FieldDefinition(8, Label = "DownloadClientSettingsInitialState", Type = FieldType.Select, SelectOptions = typeof(UTorrentState), HelpText = "DownloadClientSettingsInitialStateHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "DownloadClientSettingsInitialState", "clientName", "uTorrent")]
|
||||
public int IntialState { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -18,6 +18,7 @@ using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
@@ -43,7 +44,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new AnidubParser(Settings, Capabilities.Categories, RateLimit, _httpClient, _logger);
|
||||
return new AnidubParser(Definition, Settings, Capabilities.Categories, RateLimit, _httpClient, _logger);
|
||||
}
|
||||
|
||||
protected override async Task DoLogin()
|
||||
@@ -244,6 +245,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public class AnidubParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly ProviderDefinition _definition;
|
||||
private readonly UserPassTorrentBaseSettings _settings;
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
private readonly TimeSpan _rateLimit;
|
||||
@@ -270,8 +272,9 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{ "/anons_ongoing", "12" }
|
||||
};
|
||||
|
||||
public AnidubParser(UserPassTorrentBaseSettings settings, IndexerCapabilitiesCategories categories, TimeSpan rateLimit, IIndexerHttpClient httpClient, Logger logger)
|
||||
public AnidubParser(ProviderDefinition definition, UserPassTorrentBaseSettings settings, IndexerCapabilitiesCategories categories, TimeSpan rateLimit, IIndexerHttpClient httpClient, Logger logger)
|
||||
{
|
||||
_definition = definition;
|
||||
_settings = settings;
|
||||
_categories = categories;
|
||||
_rateLimit = rateLimit;
|
||||
@@ -479,7 +482,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
.Build();
|
||||
|
||||
var releaseIndexerRequest = new IndexerRequest(releaseRequest);
|
||||
var releaseResponse = new IndexerResponse(releaseIndexerRequest, _httpClient.Execute(releaseIndexerRequest.HttpRequest));
|
||||
var releaseResponse = new IndexerResponse(releaseIndexerRequest, _httpClient.ExecuteProxied(releaseIndexerRequest.HttpRequest, _definition));
|
||||
|
||||
// Throw common http errors here before we try to parse
|
||||
if (releaseResponse.HttpResponse.HasHttpError)
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
MusicSearchParam.Q, MusicSearchParam.Artist, MusicSearchParam.Album, MusicSearchParam.Year
|
||||
},
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
@@ -209,6 +209,24 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
}
|
||||
}
|
||||
|
||||
if (searchType == "music" && searchCriteria is MusicSearchCriteria musicSearchCriteria)
|
||||
{
|
||||
if (musicSearchCriteria.Artist.IsNotNullOrWhiteSpace() && musicSearchCriteria.Artist != "VA")
|
||||
{
|
||||
parameters.Set("artistnames", musicSearchCriteria.Artist);
|
||||
}
|
||||
|
||||
if (musicSearchCriteria.Album.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
parameters.Set("groupname", musicSearchCriteria.Album);
|
||||
}
|
||||
|
||||
if (musicSearchCriteria.Year is > 0)
|
||||
{
|
||||
parameters.Set("year", musicSearchCriteria.Year.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
var queryCats = _capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories);
|
||||
|
||||
if (queryCats.Any())
|
||||
|
||||
@@ -14,6 +14,7 @@ using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
@@ -40,7 +41,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new AnimediaParser(Settings, Capabilities.Categories, RateLimit, _httpClient);
|
||||
return new AnimediaParser(Definition, Settings, Capabilities.Categories, RateLimit, _httpClient);
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
@@ -144,6 +145,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public class AnimediaParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly ProviderDefinition _definition;
|
||||
private readonly NoAuthTorrentBaseSettings _settings;
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
private readonly TimeSpan _rateLimit;
|
||||
@@ -157,8 +159,9 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
private static readonly Regex CategorieOVARegex = new Regex(@"ОВА|OVA|ОНА|ONA|Special", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex CategorieDoramaRegex = new Regex(@"Дорама", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
public AnimediaParser(NoAuthTorrentBaseSettings settings, IndexerCapabilitiesCategories categories, TimeSpan rateLimit, IIndexerHttpClient httpClient)
|
||||
public AnimediaParser(ProviderDefinition definition, NoAuthTorrentBaseSettings settings, IndexerCapabilitiesCategories categories, TimeSpan rateLimit, IIndexerHttpClient httpClient)
|
||||
{
|
||||
_definition = definition;
|
||||
_settings = settings;
|
||||
_categories = categories;
|
||||
_rateLimit = rateLimit;
|
||||
@@ -311,7 +314,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
.Build();
|
||||
|
||||
var releaseIndexerRequest = new IndexerRequest(releaseRequest);
|
||||
var releaseResponse = new IndexerResponse(releaseIndexerRequest, _httpClient.Execute(releaseIndexerRequest.HttpRequest));
|
||||
var releaseResponse = new IndexerResponse(releaseIndexerRequest, _httpClient.ExecuteProxied(releaseIndexerRequest.HttpRequest, _definition));
|
||||
|
||||
// Throw common http errors here before we try to parse
|
||||
if (releaseResponse.HttpResponse.HasHttpError)
|
||||
|
||||
@@ -1,280 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Html.Parser;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Indexers.Settings;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
[Obsolete("Moved to YML for Cardigann")]
|
||||
public class Anthelion : TorrentIndexerBase<UserPassTorrentBaseSettings>
|
||||
{
|
||||
public override string Name => "Anthelion";
|
||||
public override string[] IndexerUrls => new string[] { "https://anthelion.me/" };
|
||||
private string LoginUrl => Settings.BaseUrl + "login.php";
|
||||
public override string Description => "A movies tracker";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
public Anthelion(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new AnthelionRequestGenerator() { Settings = Settings, Capabilities = Capabilities };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new AnthelionParser(Settings, Capabilities.Categories);
|
||||
}
|
||||
|
||||
protected override async Task DoLogin()
|
||||
{
|
||||
var requestBuilder = new HttpRequestBuilder(LoginUrl)
|
||||
{
|
||||
LogResponseContent = true,
|
||||
AllowAutoRedirect = true,
|
||||
Method = HttpMethod.Post
|
||||
};
|
||||
|
||||
var cookies = Cookies;
|
||||
Cookies = null;
|
||||
|
||||
var authLoginRequest = requestBuilder
|
||||
.AddFormParameter("username", Settings.Username)
|
||||
.AddFormParameter("password", Settings.Password)
|
||||
.AddFormParameter("keeplogged", "1")
|
||||
.AddFormParameter("login", "Log+In!")
|
||||
.SetHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||
.SetHeader("Referer", LoginUrl)
|
||||
.Build();
|
||||
|
||||
var response = await ExecuteAuth(authLoginRequest);
|
||||
|
||||
if (CheckIfLoginNeeded(response))
|
||||
{
|
||||
var parser = new HtmlParser();
|
||||
using var dom = parser.ParseDocument(response.Content);
|
||||
var errorMessage = dom.QuerySelector("form#loginform")?.TextContent.Trim();
|
||||
|
||||
throw new IndexerAuthException(errorMessage ?? "Unknown error message, please report.");
|
||||
}
|
||||
|
||||
cookies = response.GetCookies();
|
||||
UpdateCookies(cookies, DateTime.Now.AddDays(30));
|
||||
|
||||
_logger.Debug("Anthelion authentication succeeded.");
|
||||
}
|
||||
|
||||
protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
|
||||
{
|
||||
return !httpResponse.Content.Contains("logout.php");
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping("1", NewznabStandardCategory.Movies, "Film/Feature");
|
||||
caps.Categories.AddCategoryMapping("2", NewznabStandardCategory.Movies, "Film/Short");
|
||||
caps.Categories.AddCategoryMapping("3", NewznabStandardCategory.TV, "TV/Miniseries");
|
||||
caps.Categories.AddCategoryMapping("4", NewznabStandardCategory.Other, "Other");
|
||||
|
||||
return caps;
|
||||
}
|
||||
}
|
||||
|
||||
public class AnthelionRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
public UserPassTorrentBaseSettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories, string imdbId = null)
|
||||
{
|
||||
var searchUrl = string.Format("{0}/torrents.php", Settings.BaseUrl.TrimEnd('/'));
|
||||
|
||||
// TODO: IMDB search is available but it requires to parse the details page
|
||||
var qc = new NameValueCollection
|
||||
{
|
||||
{ "order_by", "time" },
|
||||
{ "order_way", "desc" },
|
||||
{ "action", "basic" },
|
||||
{ "searchsubmit", "1" },
|
||||
{ "searchstr", imdbId.IsNotNullOrWhiteSpace() ? imdbId : term.Replace(".", " ") }
|
||||
};
|
||||
|
||||
var catList = Capabilities.Categories.MapTorznabCapsToTrackers(categories);
|
||||
|
||||
foreach (var cat in catList)
|
||||
{
|
||||
qc.Add($"filter_cat[{cat}]", "1");
|
||||
}
|
||||
|
||||
searchUrl = searchUrl + "?" + qc.GetQueryString();
|
||||
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Html);
|
||||
|
||||
yield return request;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
public class AnthelionParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly UserPassTorrentBaseSettings _settings;
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
|
||||
public AnthelionParser(UserPassTorrentBaseSettings settings, IndexerCapabilitiesCategories categories)
|
||||
{
|
||||
_settings = settings;
|
||||
_categories = categories;
|
||||
}
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var torrentInfos = new List<ReleaseInfo>();
|
||||
|
||||
var parser = new HtmlParser();
|
||||
using var doc = parser.ParseDocument(indexerResponse.Content);
|
||||
var rows = doc.QuerySelectorAll("table.torrent_table > tbody > tr.torrent");
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var qDetailsLink = row.QuerySelector("a.torrent_name");
|
||||
var year = qDetailsLink.NextSibling.TextContent.Replace("[", "").Replace("]", "").Trim();
|
||||
var tags = row.QuerySelector("div.torrent_info").FirstChild.TextContent.Replace(" / ", " ").Trim();
|
||||
var title = $"{qDetailsLink.TextContent} {year} {tags}";
|
||||
var description = row.QuerySelector("div.tags").TextContent.Trim();
|
||||
var details = _settings.BaseUrl + qDetailsLink.GetAttribute("href");
|
||||
var torrentId = qDetailsLink.GetAttribute("href").Split('=').Last();
|
||||
var link = _settings.BaseUrl + "torrents.php?action=download&id=" + torrentId;
|
||||
var posterStr = qDetailsLink.GetAttribute("data-cover");
|
||||
var poster = !string.IsNullOrWhiteSpace(posterStr) ? posterStr : null;
|
||||
|
||||
var files = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(3)").TextContent);
|
||||
var publishDate = DateTimeUtil.FromTimeAgo(row.QuerySelector("td:nth-child(4)").TextContent);
|
||||
var size = ParseUtil.GetBytes(row.QuerySelector("td:nth-child(5)").FirstChild.TextContent);
|
||||
var grabs = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(6)").TextContent);
|
||||
var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(7)").TextContent);
|
||||
var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(8)").TextContent);
|
||||
|
||||
var dlVolumeFactor = row.QuerySelector("strong.tl_free") != null ? 0 : 1;
|
||||
|
||||
var cat = row.QuerySelector("td.cats_col > div").GetAttribute("class").Replace("tooltip cats_", "");
|
||||
var category = new List<IndexerCategory>
|
||||
{
|
||||
cat switch
|
||||
{
|
||||
"featurefilm" => NewznabStandardCategory.Movies,
|
||||
"shortfilm" => NewznabStandardCategory.Movies,
|
||||
"miniseries" => NewznabStandardCategory.TV,
|
||||
"other" => NewznabStandardCategory.Other,
|
||||
_ => throw new Exception($"Unknown category: {cat}")
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: TMDb is also available
|
||||
var qImdb = row.QuerySelector("a[href^=\"https://www.imdb.com\"]");
|
||||
var imdb = qImdb != null ? ParseUtil.GetImdbId(qImdb.GetAttribute("href").Split('/').Last()) : null;
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 259200,
|
||||
Description = description,
|
||||
Title = title,
|
||||
PublishDate = publishDate,
|
||||
Categories = category,
|
||||
DownloadUrl = link,
|
||||
InfoUrl = details,
|
||||
PosterUrl = poster,
|
||||
Guid = link,
|
||||
ImdbId = imdb.GetValueOrDefault(),
|
||||
Seeders = seeders,
|
||||
Peers = leechers + seeders,
|
||||
Size = size,
|
||||
Grabs = grabs,
|
||||
Files = files,
|
||||
DownloadVolumeFactor = dlVolumeFactor,
|
||||
UploadVolumeFactor = 1
|
||||
};
|
||||
|
||||
torrentInfos.Add(release);
|
||||
}
|
||||
|
||||
return torrentInfos.ToArray();
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,299 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Html.Dom;
|
||||
using AngleSharp.Html.Parser;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Settings;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions;
|
||||
|
||||
[Obsolete("User Agent blocked")]
|
||||
public class AudioBookBay : TorrentIndexerBase<NoAuthTorrentBaseSettings>
|
||||
{
|
||||
public override string Name => "AudioBook Bay";
|
||||
public override string[] IndexerUrls => new[]
|
||||
{
|
||||
"https://audiobookbay.is/"
|
||||
};
|
||||
public override string[] LegacyUrls => new[]
|
||||
{
|
||||
"https://audiobookbay.la/",
|
||||
"http://audiobookbay.net/",
|
||||
"https://audiobookbay.unblockit.tv/",
|
||||
"http://audiobookbay.nl/",
|
||||
"http://audiobookbay.ws/",
|
||||
"https://audiobookbay.unblockit.how/",
|
||||
"https://audiobookbay.unblockit.cam/",
|
||||
"https://audiobookbay.unblockit.biz/",
|
||||
"https://audiobookbay.unblockit.day/",
|
||||
"https://audiobookbay.unblockit.llc/",
|
||||
"https://audiobookbay.unblockit.blue/",
|
||||
"https://audiobookbay.unblockit.name/",
|
||||
"http://audiobookbay.fi/",
|
||||
"http://audiobookbay.se/",
|
||||
"http://audiobookbayabb.com/",
|
||||
"https://audiobookbay.unblockit.ist/",
|
||||
"https://audiobookbay.unblockit.bet/",
|
||||
"https://audiobookbay.unblockit.cat/",
|
||||
"https://audiobookbay.unblockit.nz/",
|
||||
"https://audiobookbay.fi/",
|
||||
"https://audiobookbay.unblockit.page/",
|
||||
"https://audiobookbay.unblockit.pet/",
|
||||
"https://audiobookbay.unblockit.ink/",
|
||||
"https://audiobookbay.unblockit.bio/", // error 502
|
||||
"https://audiobookbay.li/",
|
||||
"https://audiobookbay.se/" // redirects to .is but has invalid CA
|
||||
};
|
||||
public override string Description => "AudioBook Bay (ABB) is a public Torrent Tracker for AUDIOBOOKS";
|
||||
public override string Language => "en-US";
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Public;
|
||||
public override int PageSize => 15;
|
||||
public override TimeSpan RateLimit => TimeSpan.FromSeconds(5);
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
public AudioBookBay(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new AudioBookBayRequestGenerator(Settings);
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new AudioBookBayParser(Settings, Capabilities.Categories);
|
||||
}
|
||||
|
||||
public override async Task<byte[]> Download(Uri link)
|
||||
{
|
||||
var request = new HttpRequestBuilder(link.ToString())
|
||||
.SetCookies(GetCookies() ?? new Dictionary<string, string>())
|
||||
.Accept(HttpAccept.Html)
|
||||
.Build();
|
||||
|
||||
var response = await _httpClient.ExecuteProxiedAsync(request, Definition);
|
||||
|
||||
var parser = new HtmlParser();
|
||||
using var dom = parser.ParseDocument(response.Content);
|
||||
|
||||
var hash = dom.QuerySelector("td:contains(\"Info Hash:\") ~ td")?.TextContent.Trim();
|
||||
if (hash == null)
|
||||
{
|
||||
throw new Exception($"Failed to fetch hash from {link}");
|
||||
}
|
||||
|
||||
var title = dom.QuerySelector("div.postTitle h1")?.TextContent.Trim();
|
||||
if (title == null)
|
||||
{
|
||||
throw new Exception($"Failed to fetch title from {link}");
|
||||
}
|
||||
|
||||
title = StringUtil.MakeValidFileName(title, '_', false);
|
||||
|
||||
var magnet = MagnetLinkBuilder.BuildPublicMagnetLink(hash, title);
|
||||
|
||||
return await base.Download(new Uri(magnet));
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.AudioAudiobook);
|
||||
|
||||
return caps;
|
||||
}
|
||||
}
|
||||
|
||||
public class AudioBookBayRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
private readonly NoAuthTorrentBaseSettings _settings;
|
||||
|
||||
public AudioBookBayRequestGenerator(NoAuthTorrentBaseSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
return new IndexerPageableRequestChain();
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
||||
{
|
||||
return new IndexerPageableRequestChain();
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
|
||||
{
|
||||
return new IndexerPageableRequestChain();
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}"));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}"));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term)
|
||||
{
|
||||
var searchUrl = _settings.BaseUrl;
|
||||
|
||||
var parameters = new NameValueCollection();
|
||||
|
||||
term = Regex.Replace(term, @"[\W]+", " ").Trim();
|
||||
|
||||
if (term.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
parameters.Set("s", term);
|
||||
parameters.Set("tt", "1");
|
||||
}
|
||||
|
||||
if (parameters.Count > 0)
|
||||
{
|
||||
searchUrl += $"?{parameters.GetQueryString()}";
|
||||
}
|
||||
|
||||
yield return new IndexerRequest(new UriBuilder(searchUrl) { Path = "/" }.Uri.AbsoluteUri, HttpAccept.Html);
|
||||
}
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
public class AudioBookBayParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly NoAuthTorrentBaseSettings _settings;
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
|
||||
public AudioBookBayParser(NoAuthTorrentBaseSettings settings, IndexerCapabilitiesCategories categories)
|
||||
{
|
||||
_settings = settings;
|
||||
_categories = categories;
|
||||
}
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var releaseInfos = new List<ReleaseInfo>();
|
||||
|
||||
using var doc = ParseHtmlDocument(indexerResponse.Content);
|
||||
|
||||
var rows = doc.QuerySelectorAll("div.post:has(div[class=\"postTitle\"])");
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var infoUrl = _settings.BaseUrl + row.QuerySelector("div.postTitle h2 a")?.GetAttribute("href")?.Trim().TrimStart('/');
|
||||
|
||||
var title = row.QuerySelector("div.postTitle")?.TextContent.Trim();
|
||||
|
||||
var infoString = row.QuerySelector("div.postContent")?.TextContent.Trim() ?? string.Empty;
|
||||
|
||||
var matchFormat = Regex.Match(infoString, @"Format: (.+) \/", RegexOptions.IgnoreCase);
|
||||
if (matchFormat.Groups[1].Success && matchFormat.Groups[1].Value.Length > 0 && matchFormat.Groups[1].Value != "?")
|
||||
{
|
||||
title += $" [{matchFormat.Groups[1].Value.Trim()}]";
|
||||
}
|
||||
|
||||
var matchBitrate = Regex.Match(infoString, @"Bitrate: (.+)File", RegexOptions.IgnoreCase);
|
||||
if (matchBitrate.Groups[1].Success && matchBitrate.Groups[1].Value.Length > 0 && matchBitrate.Groups[1].Value != "?")
|
||||
{
|
||||
title += $" [{matchBitrate.Groups[1].Value.Trim()}]";
|
||||
}
|
||||
|
||||
var matchSize = Regex.Match(infoString, @"File Size: (.+?)s?$", RegexOptions.IgnoreCase);
|
||||
var size = matchSize.Groups[1].Success ? ParseUtil.GetBytes(matchSize.Groups[1].Value) : 0;
|
||||
|
||||
var matchDateAdded = Regex.Match(infoString, @"Posted: (\d{1,2} \D{3} \d{4})", RegexOptions.IgnoreCase);
|
||||
var publishDate = matchDateAdded.Groups[1].Success && DateTime.TryParseExact(matchDateAdded.Groups[1].Value, "d MMM yyyy", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var parsedDate) ? parsedDate : DateTime.Now;
|
||||
|
||||
var postInfo = row.QuerySelector("div.postInfo")?.FirstChild?.TextContent.Trim().Replace("\xA0", ";") ?? string.Empty;
|
||||
var matchCategory = Regex.Match(postInfo, @"Category: (.+)$", RegexOptions.IgnoreCase);
|
||||
var genres = matchCategory.Groups[1].Success ? matchCategory.Groups[1].Value.Split(';', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToList() : new List<string>();
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Guid = infoUrl,
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = infoUrl,
|
||||
Title = CleanTitle(title),
|
||||
Categories = new List<IndexerCategory> { NewznabStandardCategory.AudioAudiobook },
|
||||
Size = size,
|
||||
Seeders = 1,
|
||||
Peers = 1,
|
||||
PublishDate = publishDate,
|
||||
DownloadVolumeFactor = 0,
|
||||
UploadVolumeFactor = 1,
|
||||
Genres = genres
|
||||
};
|
||||
|
||||
var cover = row.QuerySelector("img[src]")?.GetAttribute("src")?.Trim();
|
||||
if (!string.IsNullOrEmpty(cover))
|
||||
{
|
||||
release.PosterUrl = cover.StartsWith("http") ? cover : _settings.BaseUrl + cover;
|
||||
}
|
||||
|
||||
releaseInfos.Add(release);
|
||||
}
|
||||
|
||||
return releaseInfos;
|
||||
}
|
||||
|
||||
private static IHtmlDocument ParseHtmlDocument(string response)
|
||||
{
|
||||
var parser = new HtmlParser();
|
||||
var doc = parser.ParseDocument(response);
|
||||
|
||||
var hidden = doc.QuerySelectorAll("div.post.re-ab");
|
||||
foreach (var element in hidden)
|
||||
{
|
||||
var body = doc.CreateElement("div");
|
||||
body.ClassList.Add("post");
|
||||
body.InnerHtml = Encoding.UTF8.GetString(Convert.FromBase64String(element.TextContent));
|
||||
element.Parent.ReplaceChild(body, element);
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
private static string CleanTitle(string title)
|
||||
{
|
||||
title = Regex.Replace(title, @"[\u0000-\u0008\u000A-\u001F\u0100-\uFFFF]", string.Empty, RegexOptions.Compiled);
|
||||
title = Regex.Replace(title, @"\s+", " ", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
return title.Trim();
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
@@ -70,7 +70,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
{
|
||||
_logger.Warn(ex, "Failed to authenticate with Avistaz");
|
||||
|
||||
var jsonResponse = STJson.Deserialize<AvistazErrorResponse>(ex.Response.Content);
|
||||
STJson.TryDeserialize<AvistazErrorResponse>(ex.Response.Content, out var jsonResponse);
|
||||
throw new IndexerAuthException(jsonResponse?.Message ?? "Unauthorized request to indexer");
|
||||
}
|
||||
}
|
||||
@@ -98,8 +98,8 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
{
|
||||
_logger.Warn(ex, "Unauthorized request to indexer");
|
||||
|
||||
var jsonResponse = new HttpResponse<AvistazErrorResponse>(ex.Response);
|
||||
return new ValidationFailure(string.Empty, jsonResponse.Resource?.Message ?? "Unauthorized request to indexer");
|
||||
STJson.TryDeserialize<AvistazErrorResponse>(ex.Response.Content, out var jsonResponse);
|
||||
return new ValidationFailure(string.Empty, jsonResponse?.Message ?? "Unauthorized request to indexer");
|
||||
}
|
||||
|
||||
_logger.Warn(ex, "Unable to connect to indexer");
|
||||
@@ -134,7 +134,10 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
|
||||
var response = await ExecuteAuth(authLoginRequest);
|
||||
|
||||
var authResponse = STJson.Deserialize<AvistazAuthResponse>(response.Content);
|
||||
if (!STJson.TryDeserialize<AvistazAuthResponse>(response.Content, out var authResponse))
|
||||
{
|
||||
throw new Exception("Invalid response from AvistaZ, the response is not valid JSON");
|
||||
}
|
||||
|
||||
return authResponse.Token;
|
||||
}
|
||||
|
||||
@@ -1,315 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Dom;
|
||||
using AngleSharp.Html.Parser;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Indexers.Settings;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
[Obsolete("Site has shutdown")]
|
||||
public class BB : TorrentIndexerBase<UserPassTorrentBaseSettings>
|
||||
{
|
||||
public override string Name => "BB";
|
||||
public override string[] IndexerUrls => new[] { Base64Extensions.FromBase64("aHR0cHM6Ly9iYWNvbmJpdHMub3JnLw==") };
|
||||
private string LoginUrl => Settings.BaseUrl + "login.php";
|
||||
public override string Description => "BB is a Private Torrent Tracker for 0DAY / GENERAL";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
public BB(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new BBRequestGenerator { Settings = Settings, Capabilities = Capabilities };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new BBParser(Settings, Capabilities.Categories);
|
||||
}
|
||||
|
||||
protected override async Task DoLogin()
|
||||
{
|
||||
var requestBuilder = new HttpRequestBuilder(LoginUrl)
|
||||
{
|
||||
LogResponseContent = true,
|
||||
AllowAutoRedirect = true,
|
||||
Method = HttpMethod.Post
|
||||
};
|
||||
|
||||
var cookies = Cookies;
|
||||
Cookies = null;
|
||||
|
||||
var authLoginRequest = requestBuilder
|
||||
.AddFormParameter("username", Settings.Username)
|
||||
.AddFormParameter("password", Settings.Password)
|
||||
.AddFormParameter("keeplogged", "1")
|
||||
.AddFormParameter("login", "Log+In!")
|
||||
.SetHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||
.SetHeader("Referer", LoginUrl)
|
||||
.Build();
|
||||
|
||||
var response = await ExecuteAuth(authLoginRequest);
|
||||
|
||||
if (CheckIfLoginNeeded(response))
|
||||
{
|
||||
var parser = new HtmlParser();
|
||||
using var dom = parser.ParseDocument(response.Content);
|
||||
var messageEl = dom.QuerySelectorAll("#loginform");
|
||||
var messages = new List<string>();
|
||||
for (var i = 0; i < 13; i++)
|
||||
{
|
||||
var child = messageEl[0].ChildNodes[i];
|
||||
messages.Add(child.Text().Trim());
|
||||
}
|
||||
|
||||
var message = string.Join(" ", messages);
|
||||
|
||||
throw new IndexerAuthException(message);
|
||||
}
|
||||
|
||||
cookies = response.GetCookies();
|
||||
UpdateCookies(cookies, DateTime.Now.AddDays(30));
|
||||
|
||||
_logger.Debug("BB authentication succeeded.");
|
||||
}
|
||||
|
||||
protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
|
||||
{
|
||||
return !httpResponse.Content.Contains("logout.php");
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Audio);
|
||||
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.AudioMP3);
|
||||
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.AudioLossless);
|
||||
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.PC);
|
||||
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.BooksEBook);
|
||||
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.AudioAudiobook);
|
||||
caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.Other);
|
||||
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.BooksMags);
|
||||
caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.BooksComics);
|
||||
caps.Categories.AddCategoryMapping(8, NewznabStandardCategory.TVAnime);
|
||||
caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.Movies);
|
||||
caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.TVHD);
|
||||
caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.TVSD);
|
||||
caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.TV);
|
||||
caps.Categories.AddCategoryMapping(11, NewznabStandardCategory.PCGames);
|
||||
caps.Categories.AddCategoryMapping(12, NewznabStandardCategory.Console);
|
||||
caps.Categories.AddCategoryMapping(13, NewznabStandardCategory.Other);
|
||||
caps.Categories.AddCategoryMapping(14, NewznabStandardCategory.Other);
|
||||
|
||||
return caps;
|
||||
}
|
||||
}
|
||||
|
||||
public class BBRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
public UserPassTorrentBaseSettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories)
|
||||
{
|
||||
var searchUrl = string.Format("{0}/torrents.php", Settings.BaseUrl.TrimEnd('/'));
|
||||
|
||||
// TODO: IMDB search is available but it requires to parse the details page
|
||||
var qc = new NameValueCollection
|
||||
{
|
||||
{ "order_by", "s3" },
|
||||
{ "order_way", "desc" },
|
||||
{ "disablegrouping", "1" },
|
||||
{ "searchtags", "" },
|
||||
{ "tags_type", "0" },
|
||||
{ "action", "basic" },
|
||||
{ "searchstr", term.Replace(".", " ") }
|
||||
};
|
||||
|
||||
var catList = Capabilities.Categories.MapTorznabCapsToTrackers(categories);
|
||||
|
||||
foreach (var cat in catList)
|
||||
{
|
||||
qc.Add($"filter_cat[{cat}]", "1");
|
||||
}
|
||||
|
||||
searchUrl = searchUrl + "?" + qc.GetQueryString();
|
||||
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Html);
|
||||
|
||||
yield return request;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
public class BBParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly UserPassTorrentBaseSettings _settings;
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
|
||||
public BBParser(UserPassTorrentBaseSettings settings, IndexerCapabilitiesCategories categories)
|
||||
{
|
||||
_settings = settings;
|
||||
_categories = categories;
|
||||
}
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var torrentInfos = new List<ReleaseInfo>();
|
||||
|
||||
var parser = new HtmlParser();
|
||||
using var dom = parser.ParseDocument(indexerResponse.Content);
|
||||
var rows = dom.QuerySelectorAll("#torrent_table > tbody > tr.torrent");
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var release = new TorrentInfo();
|
||||
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800; // 48 hours
|
||||
|
||||
var catStr = row.Children[0].FirstElementChild.GetAttribute("href").Split(new[] { '[', ']' })[1];
|
||||
release.Categories = _categories.MapTrackerCatToNewznab(catStr);
|
||||
|
||||
var qDetails = row.Children[1].QuerySelector("a[title='View Torrent']");
|
||||
release.InfoUrl = _settings.BaseUrl + qDetails.GetAttribute("href");
|
||||
release.Guid = release.InfoUrl;
|
||||
|
||||
var qDownload = row.Children[1].QuerySelector("a[title='Download']");
|
||||
release.DownloadUrl = _settings.BaseUrl + qDownload.GetAttribute("href");
|
||||
|
||||
var dateStr = row.Children[3].TextContent.Trim().Replace(" and", "");
|
||||
release.PublishDate = DateTimeUtil.FromTimeAgo(dateStr);
|
||||
|
||||
var sizeStr = row.Children[4].TextContent;
|
||||
release.Size = ParseUtil.GetBytes(sizeStr);
|
||||
|
||||
release.Files = ParseUtil.CoerceInt(row.Children[2].TextContent.Trim());
|
||||
release.Seeders = ParseUtil.CoerceInt(row.Children[7].TextContent.Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(row.Children[8].TextContent.Trim()) + release.Seeders;
|
||||
|
||||
var grabs = row.QuerySelector("td:nth-child(6)").TextContent;
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (row.QuerySelector("strong:contains(\"Freeleech!\")") != null)
|
||||
{
|
||||
release.DownloadVolumeFactor = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
release.DownloadVolumeFactor = 1;
|
||||
}
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
var title = row.QuerySelector("td:nth-child(2)");
|
||||
foreach (var element in title.QuerySelectorAll("span, strong, div, br"))
|
||||
{
|
||||
element.Remove();
|
||||
}
|
||||
|
||||
release.Title = ParseUtil.NormalizeMultiSpaces(title.TextContent.Replace(" - ]", "]"));
|
||||
|
||||
//change "Season #" to "S##" for TV shows
|
||||
if (catStr == "10")
|
||||
{
|
||||
release.Title = Regex.Replace(release.Title,
|
||||
@"Season (\d+)",
|
||||
m => string.Format("S{0:00}",
|
||||
int.Parse(m.Groups[1].Value)));
|
||||
}
|
||||
|
||||
torrentInfos.Add(release);
|
||||
}
|
||||
|
||||
return torrentInfos.ToArray();
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -205,7 +205,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
.AddQueryParam("request", "quick_user")
|
||||
.Build();
|
||||
|
||||
var indexResponse = await _httpClient.ExecuteAsync(request).ConfigureAwait(false);
|
||||
var indexResponse = await _httpClient.ExecuteProxiedAsync(request, Definition).ConfigureAwait(false);
|
||||
|
||||
var index = Json.Deserialize<GazelleGamesUserResponse>(indexResponse.Content);
|
||||
|
||||
@@ -412,6 +412,8 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
categories = _categories.MapTrackerCatToNewznab(torrent.Value.CategoryId.ToString()).ToArray();
|
||||
}
|
||||
|
||||
Enum.TryParse(torrent.Value.FreeTorrent, true, out GazelleGamesFreeTorrent freeTorrent);
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Guid = infoUrl,
|
||||
@@ -426,8 +428,8 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
Peers = torrent.Value.Leechers + torrent.Value.Seeders,
|
||||
PublishDate = torrent.Value.Time.ToUniversalTime(),
|
||||
Scene = torrent.Value.Scene == 1,
|
||||
DownloadVolumeFactor = torrent.Value.FreeTorrent is GazelleGamesFreeTorrent.FreeLeech or GazelleGamesFreeTorrent.Neutral || torrent.Value.LowSeedFL ? 0 : 1,
|
||||
UploadVolumeFactor = torrent.Value.FreeTorrent == GazelleGamesFreeTorrent.Neutral ? 0 : 1,
|
||||
DownloadVolumeFactor = freeTorrent is GazelleGamesFreeTorrent.FreeLeech or GazelleGamesFreeTorrent.Neutral || torrent.Value.LowSeedFL ? 0 : 1,
|
||||
UploadVolumeFactor = freeTorrent == GazelleGamesFreeTorrent.Neutral ? 0 : 1,
|
||||
MinimumSeedTime = 288000 // Minimum of 3 days and 8 hours (80 hours in total)
|
||||
};
|
||||
|
||||
@@ -588,7 +590,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public int? Snatched { get; set; }
|
||||
public int Seeders { get; set; }
|
||||
public int Leechers { get; set; }
|
||||
public GazelleGamesFreeTorrent FreeTorrent { get; set; }
|
||||
public string FreeTorrent { get; set; }
|
||||
public bool PersonalFL { get; set; }
|
||||
public bool LowSeedFL { get; set; }
|
||||
|
||||
|
||||
@@ -1,310 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using AngleSharp.Dom;
|
||||
using AngleSharp.Html.Dom;
|
||||
using AngleSharp.Html.Parser;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Settings;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions;
|
||||
|
||||
[Obsolete("Converted to Torznab")]
|
||||
public class MoreThanTV : TorrentIndexerBase<CookieTorrentBaseSettings>
|
||||
{
|
||||
public override string Name => "MoreThanTV";
|
||||
public override string[] IndexerUrls => new[] { "https://www.morethantv.me/" };
|
||||
public override string Description => "Private torrent tracker for TV / MOVIES";
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
public override bool FollowRedirect => true;
|
||||
|
||||
public MoreThanTV(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
=> new MoreThanTVRequestGenerator(Settings, Capabilities);
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
=> new MoreThanTVParser
|
||||
{
|
||||
Settings = Settings
|
||||
};
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Movies, "Movies");
|
||||
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TV, "TV");
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
protected override IDictionary<string, string> GetCookies()
|
||||
{
|
||||
return CookieUtil.CookieHeaderToDictionary(Settings.Cookie);
|
||||
}
|
||||
}
|
||||
|
||||
public class MoreThanTVRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
private CookieTorrentBaseSettings Settings { get; }
|
||||
private IndexerCapabilities Capabilities { get; }
|
||||
|
||||
private NameValueCollection BrowserHeaders { get; }
|
||||
|
||||
public MoreThanTVRequestGenerator(CookieTorrentBaseSettings settings, IndexerCapabilities capabilities)
|
||||
{
|
||||
Settings = settings;
|
||||
Capabilities = capabilities;
|
||||
BrowserHeaders = new NameValueCollection()
|
||||
{
|
||||
{ "referer", settings.BaseUrl },
|
||||
{ "Upgrade-Insecure-Requests", "1" },
|
||||
{ "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36" }
|
||||
};
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
=> PerformRequest(searchCriteria);
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
||||
=> PerformRequest(searchCriteria);
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
|
||||
=> PerformRequest(searchCriteria);
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
||||
=> PerformRequest(searchCriteria);
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
|
||||
=> PerformRequest(searchCriteria);
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
|
||||
private IndexerPageableRequestChain PerformRequest(SearchCriteriaBase query)
|
||||
{
|
||||
var chain = new IndexerPageableRequestChain();
|
||||
|
||||
var requests = new List<IndexerRequest> { new (new HttpRequest(GetTorrentSearchUrl(query)) { Headers = new HttpHeader(BrowserHeaders), AllowAutoRedirect = true }) };
|
||||
|
||||
if (query is TvSearchCriteria tvSearchCriteria)
|
||||
{
|
||||
// Always search for torrent groups (complete seasons) too
|
||||
var seasonRegex = new Regex(@".*\s[Ss]{1}\d{2}([Ee]{1}\d{2,3})?$", RegexOptions.Compiled);
|
||||
var seasonMatch = seasonRegex.Match(query.SanitizedSearchTerm);
|
||||
if (seasonMatch.Success)
|
||||
{
|
||||
var seasonReplaceRegex = new Regex(@"[Ss]{1}\d{2}([Ee]{1}\d{2,3})?", RegexOptions.Compiled);
|
||||
var newSearchQuery = seasonReplaceRegex.Replace(query.SanitizedSearchTerm, $"Season {tvSearchCriteria.Season}");
|
||||
requests.Add(new IndexerRequest(new HttpRequest(GetTorrentSearchUrl(query, newSearchQuery)) { Headers = new HttpHeader(BrowserHeaders), AllowAutoRedirect = true }));
|
||||
}
|
||||
}
|
||||
|
||||
chain.Add(requests);
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
private string GetTorrentSearchUrl(SearchCriteriaBase query, string overrideSearchTerm = null)
|
||||
{
|
||||
var qc = new NameValueCollection
|
||||
{
|
||||
{ "action", "advanced" },
|
||||
{ "sizetype", "gb" },
|
||||
{ "sizerange", "0.01" },
|
||||
{ "title", overrideSearchTerm ?? GetSearchString(query.SanitizedSearchTerm) }
|
||||
};
|
||||
|
||||
switch (query)
|
||||
{
|
||||
case MovieSearchCriteria:
|
||||
qc.Add("filter_cat[1]", "1"); // HD Movies
|
||||
qc.Add("filter_cat[2]", "1"); // SD Movies
|
||||
break;
|
||||
case TvSearchCriteria:
|
||||
qc.Add("filter_cat[3]", "1"); // HD Episode
|
||||
qc.Add("filter_cat[4]", "1"); // SD Episode
|
||||
qc.Add("filter_cat[5]", "1"); // HD Season
|
||||
qc.Add("filter_cat[6]", "1"); // SD Season
|
||||
break;
|
||||
}
|
||||
|
||||
return $"{Settings.BaseUrl}torrents/browse?{qc.GetQueryString()}";
|
||||
}
|
||||
|
||||
private string GetSearchString(string input)
|
||||
{
|
||||
input = input.Replace("Marvels", "Marvel"); // strip 's for better results
|
||||
var regex = new Regex(@"(S\d{2})$", RegexOptions.Compiled);
|
||||
return regex.Replace(input, "$1*"); // If we're just seaching for a season (no episode) append an * to include all episodes of that season.
|
||||
}
|
||||
}
|
||||
|
||||
public class MoreThanTVParser : IParseIndexerResponse
|
||||
{
|
||||
public CookieTorrentBaseSettings Settings { get; init; }
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
try
|
||||
{
|
||||
var parser = new HtmlParser();
|
||||
using var document = parser.ParseDocument(indexerResponse.Content);
|
||||
var torrents = document.QuerySelectorAll("#torrent_table > tbody > tr.torrent");
|
||||
var movies = new[] { "movie" };
|
||||
var tv = new[] { "season", "episode" };
|
||||
|
||||
// Loop through all torrents checking for groups
|
||||
foreach (var torrent in torrents)
|
||||
{
|
||||
// Parse required data
|
||||
var downloadAnchor = torrent.QuerySelector("span a[href^=\"/torrents.php?action=download\"]");
|
||||
|
||||
if (downloadAnchor == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var title = downloadAnchor.ParentElement.ParentElement.ParentElement.QuerySelector("a[class=\"overlay_torrent\"]").TextContent.Trim();
|
||||
title = CleanUpTitle(title);
|
||||
|
||||
var category = torrent.QuerySelector(".cats_col div").GetAttribute("title");
|
||||
|
||||
// default to Other
|
||||
var indexerCategory = NewznabStandardCategory.Other;
|
||||
|
||||
if (movies.Any(category.Contains))
|
||||
{
|
||||
indexerCategory = NewznabStandardCategory.Movies;
|
||||
}
|
||||
else if (tv.Any(category.Contains))
|
||||
{
|
||||
indexerCategory = NewznabStandardCategory.TV;
|
||||
}
|
||||
|
||||
releases.Add(GetReleaseInfo(torrent, downloadAnchor, title, indexerCategory));
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception("Error while parsing torrent response", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gather Release info from torrent table. Target using css
|
||||
/// </summary>
|
||||
/// <param name="row"></param>
|
||||
/// <param name="downloadAnchor"></param>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="category"></param>
|
||||
/// <returns></returns>
|
||||
private ReleaseInfo GetReleaseInfo(IElement row, IElement downloadAnchor, string title, IndexerCategory category)
|
||||
{
|
||||
// count from bottom
|
||||
const int FILES_COL = 7;
|
||||
/*const int COMMENTS_COL = 7;*/
|
||||
const int DATE_COL = 6;
|
||||
const int FILESIZE_COL = 5;
|
||||
const int SNATCHED_COL = 4;
|
||||
const int SEEDS_COL = 3;
|
||||
const int LEECHERS_COL = 2;
|
||||
/*const int USER_COL = 1;*/
|
||||
|
||||
var downloadAnchorHref = (downloadAnchor as IHtmlAnchorElement).Href;
|
||||
var queryParams = HttpUtility.ParseQueryString(downloadAnchorHref, Encoding.UTF8);
|
||||
var torrentId = queryParams["id"];
|
||||
|
||||
var qFiles = row.QuerySelector("td:nth-last-child(" + FILES_COL + ")").TextContent;
|
||||
|
||||
var fileCount = ParseUtil.CoerceInt(qFiles);
|
||||
var qPublishDate = row.QuerySelector("td:nth-last-child(" + DATE_COL + ") .time").Attributes["title"].Value;
|
||||
var publishDate = DateTime.ParseExact(qPublishDate, "MMM dd yyyy, HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime();
|
||||
var qPoster = row.QuerySelector("div.tp-banner img")?.GetAttribute("src");
|
||||
var poster = (qPoster != null && !qPoster.Contains("caticons")) ? qPoster : null;
|
||||
var description = row.QuerySelector("div.tags")?.TextContent.Trim();
|
||||
var fileSize = row.QuerySelector("td:nth-last-child(" + FILESIZE_COL + ")").TextContent.Trim();
|
||||
var snatched = row.QuerySelector("td:nth-last-child(" + SNATCHED_COL + ")").TextContent.Trim();
|
||||
var seeds = row.QuerySelector("td:nth-last-child(" + SEEDS_COL + ")").TextContent.Trim();
|
||||
var leechs = row.QuerySelector("td:nth-last-child(" + LEECHERS_COL + ")").TextContent.Trim();
|
||||
|
||||
if (fileSize.Length <= 0 || snatched.Length <= 0 || seeds.Length <= 0 || leechs.Length <= 0)
|
||||
{
|
||||
// Size (xx.xx GB[ (Max)]) Snatches (xx) Seeders (xx) Leechers (xx)
|
||||
throw new Exception($"We expected 4 torrent datas.");
|
||||
}
|
||||
|
||||
var detailUrl = $"{Settings.BaseUrl}details.php";
|
||||
|
||||
var size = ParseUtil.GetBytes(fileSize);
|
||||
var grabs = int.Parse(snatched, NumberStyles.AllowThousands, CultureInfo.InvariantCulture);
|
||||
var seeders = int.Parse(seeds, NumberStyles.AllowThousands, CultureInfo.InvariantCulture);
|
||||
var leechers = int.Parse(leechs, NumberStyles.AllowThousands, CultureInfo.InvariantCulture);
|
||||
var detailsUrl = $"{detailUrl}?torrentid={torrentId}";
|
||||
var downloadUrl = $"{detailUrl}?action=download&id={torrentId}";
|
||||
var categories = new List<IndexerCategory> { category };
|
||||
|
||||
return new TorrentInfo
|
||||
{
|
||||
Title = title,
|
||||
Categories = categories,
|
||||
DownloadUrl = downloadUrl,
|
||||
PublishDate = publishDate,
|
||||
PosterUrl = poster,
|
||||
Description = description,
|
||||
Seeders = seeders,
|
||||
Peers = seeders + leechers,
|
||||
Files = fileCount,
|
||||
Size = size,
|
||||
Grabs = grabs,
|
||||
Guid = downloadUrl,
|
||||
InfoUrl = detailsUrl,
|
||||
DownloadVolumeFactor = 0, // ratioless tracker
|
||||
UploadVolumeFactor = 1
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean Up any title stuff
|
||||
/// </summary>
|
||||
/// <param name="title"></param>
|
||||
/// <returns></returns>
|
||||
private string CleanUpTitle(string title)
|
||||
{
|
||||
return title
|
||||
.Replace(".", " ")
|
||||
.Replace("4K", "2160p"); // sonarr cleanup
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
@@ -1,292 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Html.Parser;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Indexers.Settings;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions;
|
||||
[Obsolete("PirateTheNet has shutdown 2023-10-14")]
|
||||
public class PirateTheNet : TorrentIndexerBase<UserPassTorrentBaseSettings>
|
||||
{
|
||||
public override string Name => "PirateTheNet";
|
||||
public override string[] IndexerUrls => new[] { "https://piratethenet.org/" };
|
||||
public override string[] LegacyUrls => new[] { "http://piratethenet.org/" };
|
||||
public override string Description => "PirateTheNet (PTN) is a ratioless movie tracker.";
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
private string LoginUrl => Settings.BaseUrl + "takelogin.php";
|
||||
private string CaptchaUrl => Settings.BaseUrl + "simpleCaptcha.php?numImages=1";
|
||||
|
||||
public PirateTheNet(IIndexerHttpClient httpClient,
|
||||
IEventAggregator eventAggregator,
|
||||
IIndexerStatusService indexerStatusService,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new PirateTheNetRequestGenerator(Settings, Capabilities);
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new PirateTheNetParser(Settings, Capabilities.Categories);
|
||||
}
|
||||
|
||||
protected override async Task DoLogin()
|
||||
{
|
||||
var captchaPage = await ExecuteAuth(new HttpRequest(CaptchaUrl));
|
||||
|
||||
var captchaResponse = JsonConvert.DeserializeAnonymousType(captchaPage.Content, new
|
||||
{
|
||||
images = new[] { new { hash = string.Empty } }
|
||||
});
|
||||
|
||||
var requestBuilder = new HttpRequestBuilder(LoginUrl)
|
||||
{
|
||||
LogResponseContent = true,
|
||||
AllowAutoRedirect = true,
|
||||
Method = HttpMethod.Post
|
||||
};
|
||||
|
||||
var authLoginRequest = requestBuilder
|
||||
.SetCookies(captchaPage.GetCookies())
|
||||
.AddFormParameter("username", Settings.Username)
|
||||
.AddFormParameter("password", Settings.Password)
|
||||
.AddFormParameter("captchaSelection", captchaResponse.images[0].hash)
|
||||
.SetHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||
.SetHeader("Referer", LoginUrl)
|
||||
.Build();
|
||||
|
||||
var response = await ExecuteAuth(authLoginRequest);
|
||||
|
||||
if (CheckIfLoginNeeded(response))
|
||||
{
|
||||
throw new IndexerAuthException("Login Failed.");
|
||||
}
|
||||
|
||||
var cookies = response.GetCookies();
|
||||
UpdateCookies(cookies, DateTime.Now.AddDays(30));
|
||||
|
||||
_logger.Debug("Authentication succeeded.");
|
||||
}
|
||||
|
||||
protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
|
||||
{
|
||||
return !httpResponse.Content.Contains("logout.php");
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping("1080P", NewznabStandardCategory.MoviesHD, "1080P");
|
||||
caps.Categories.AddCategoryMapping("2160P", NewznabStandardCategory.MoviesHD, "2160P");
|
||||
caps.Categories.AddCategoryMapping("720P", NewznabStandardCategory.MoviesHD, "720P");
|
||||
caps.Categories.AddCategoryMapping("BDRip", NewznabStandardCategory.MoviesSD, "BDRip");
|
||||
caps.Categories.AddCategoryMapping("BluRay", NewznabStandardCategory.MoviesBluRay, "BluRay");
|
||||
caps.Categories.AddCategoryMapping("BRRip", NewznabStandardCategory.MoviesSD, "BRRip");
|
||||
caps.Categories.AddCategoryMapping("DVDR", NewznabStandardCategory.MoviesDVD, "DVDR");
|
||||
caps.Categories.AddCategoryMapping("DVDRip", NewznabStandardCategory.MoviesSD, "DVDRip");
|
||||
caps.Categories.AddCategoryMapping("FLAC", NewznabStandardCategory.AudioLossless, "FLAC OST");
|
||||
caps.Categories.AddCategoryMapping("MP3", NewznabStandardCategory.AudioMP3, "MP3 OST");
|
||||
caps.Categories.AddCategoryMapping("MP4", NewznabStandardCategory.MoviesOther, "MP4");
|
||||
caps.Categories.AddCategoryMapping("Packs", NewznabStandardCategory.MoviesOther, "Packs");
|
||||
caps.Categories.AddCategoryMapping("R5", NewznabStandardCategory.MoviesDVD, "R5 / SCR");
|
||||
caps.Categories.AddCategoryMapping("Remux", NewznabStandardCategory.MoviesOther, "Remux");
|
||||
caps.Categories.AddCategoryMapping("TVRip", NewznabStandardCategory.MoviesOther, "TVRip");
|
||||
caps.Categories.AddCategoryMapping("WebRip", NewznabStandardCategory.MoviesWEBDL, "WebRip");
|
||||
|
||||
return caps;
|
||||
}
|
||||
}
|
||||
|
||||
public class PirateTheNetRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
private readonly UserPassTorrentBaseSettings _settings;
|
||||
private readonly IndexerCapabilities _capabilities;
|
||||
|
||||
public PirateTheNetRequestGenerator(UserPassTorrentBaseSettings settings, IndexerCapabilities capabilities)
|
||||
{
|
||||
_settings = settings;
|
||||
_capabilities = capabilities;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
||||
{
|
||||
return new IndexerPageableRequestChain();
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
|
||||
{
|
||||
return new IndexerPageableRequestChain();
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
||||
{
|
||||
return new IndexerPageableRequestChain();
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories, string imdbId = null)
|
||||
{
|
||||
var parameters = new NameValueCollection
|
||||
{
|
||||
{ "action", "torrentstable" },
|
||||
{ "viewtype", "0" },
|
||||
{ "visiblecategories", "Action,Adventure,Animation,Biography,Comedy,Crime,Documentary,Drama,Family,Fantasy,History,Horror,Kids,Music,Mystery,Packs,Romance,Sci-Fi,Short,Sports,Thriller,War,Western" },
|
||||
{ "page", "1" },
|
||||
{ "visibility", "showall" },
|
||||
{ "compression", "showall" },
|
||||
{ "sort", "added" },
|
||||
{ "order", "DESC" },
|
||||
{ "titleonly", "true" },
|
||||
{ "packs", "showall" },
|
||||
{ "bookmarks", "showall" },
|
||||
{ "subscriptions", "showall" },
|
||||
{ "skw", "showall" }
|
||||
};
|
||||
|
||||
if (imdbId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
parameters.Set("advancedsearchparameters", $"[imdb={imdbId}]");
|
||||
}
|
||||
else if (term.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
parameters.Set("searchstring", term);
|
||||
}
|
||||
|
||||
var queryCats = _capabilities.Categories.MapTorznabCapsToTrackers(categories);
|
||||
if (queryCats.Any())
|
||||
{
|
||||
parameters.Set("hiddenqualities", string.Join(",", queryCats));
|
||||
}
|
||||
|
||||
var searchUrl = _settings.BaseUrl + "torrentsutils.php";
|
||||
|
||||
if (parameters.Count > 0)
|
||||
{
|
||||
searchUrl += $"?{parameters.GetQueryString()}";
|
||||
}
|
||||
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Html);
|
||||
|
||||
yield return request;
|
||||
}
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
public class PirateTheNetParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly UserPassTorrentBaseSettings _settings;
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
|
||||
public PirateTheNetParser(UserPassTorrentBaseSettings settings, IndexerCapabilitiesCategories categories)
|
||||
{
|
||||
_settings = settings;
|
||||
_categories = categories;
|
||||
}
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var releaseInfos = new List<ReleaseInfo>();
|
||||
|
||||
var parser = new HtmlParser();
|
||||
using var dom = parser.ParseDocument(indexerResponse.Content);
|
||||
|
||||
var rows = dom.QuerySelectorAll("table.main > tbody > tr");
|
||||
foreach (var row in rows.Skip(1))
|
||||
{
|
||||
var qDetails = row.QuerySelector("td:nth-of-type(2) > a:nth-of-type(1)");
|
||||
var title = qDetails?.GetAttribute("alt")?.Trim();
|
||||
|
||||
var infoUrl = _settings.BaseUrl + qDetails?.GetAttribute("href")?.TrimStart('/');
|
||||
var downloadUrl = _settings.BaseUrl + row.QuerySelector("td > a:has(img[alt=\"Download Torrent\"])")?.GetAttribute("href")?.TrimStart('/');
|
||||
|
||||
var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(9)")?.TextContent);
|
||||
var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(10)")?.TextContent);
|
||||
|
||||
var cat = row.QuerySelector("td:nth-of-type(1) > a > img")?.GetAttribute("src")?.Split('/').Last().Split('.').First() ?? "packs";
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Guid = infoUrl,
|
||||
InfoUrl = infoUrl,
|
||||
DownloadUrl = downloadUrl,
|
||||
Title = title,
|
||||
Categories = _categories.MapTrackerCatToNewznab(cat),
|
||||
Seeders = seeders,
|
||||
Peers = seeders + leechers,
|
||||
Size = ParseUtil.GetBytes(row.QuerySelector("td:nth-of-type(7)")?.TextContent.Trim()),
|
||||
Files = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(4)")?.TextContent),
|
||||
Grabs = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(8)")?.TextContent),
|
||||
DownloadVolumeFactor = 0, // ratioless
|
||||
UploadVolumeFactor = 1,
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 259200, // 72 hours
|
||||
};
|
||||
|
||||
var added = row.QuerySelector("td:nth-of-type(6) > nobr")?.TextContent.Trim();
|
||||
if (added.StartsWith("Today "))
|
||||
{
|
||||
release.PublishDate = DateTime.Now.Date + DateTime.ParseExact(added.Split(" ", 2).Last(), "hh:mm tt", CultureInfo.InvariantCulture).TimeOfDay;
|
||||
}
|
||||
else if (added.StartsWith("Yesterday "))
|
||||
{
|
||||
release.PublishDate = DateTime.Now.AddDays(-1).Date + DateTime.ParseExact(added.Split(" ", 2).Last(), "hh:mm tt", CultureInfo.InvariantCulture).TimeOfDay;
|
||||
}
|
||||
else
|
||||
{
|
||||
release.PublishDate = DateTime.ParseExact(added, "MMM d yyyy hh:mm tt", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
releaseInfos.Add(release);
|
||||
}
|
||||
|
||||
return releaseInfos.ToArray();
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
@@ -19,6 +19,7 @@ using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions;
|
||||
@@ -50,7 +51,7 @@ public class Shazbat : TorrentIndexerBase<ShazbatSettings>
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new ShazbatParser(Settings, RateLimit, _httpClient, _logger);
|
||||
return new ShazbatParser(Definition, Settings, RateLimit, _httpClient, _logger);
|
||||
}
|
||||
|
||||
protected override async Task DoLogin()
|
||||
@@ -101,7 +102,7 @@ public class Shazbat : TorrentIndexerBase<ShazbatSettings>
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
Flags = new List<IndexerFlag>
|
||||
{
|
||||
@@ -202,6 +203,7 @@ public class ShazbatRequestGenerator : IIndexerRequestGenerator
|
||||
|
||||
public class ShazbatParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly ProviderDefinition _definition;
|
||||
private readonly ShazbatSettings _settings;
|
||||
private readonly TimeSpan _rateLimit;
|
||||
private readonly IIndexerHttpClient _httpClient;
|
||||
@@ -210,8 +212,9 @@ public class ShazbatParser : IParseIndexerResponse
|
||||
private readonly Regex _torrentInfoRegex = new (@"\((?<size>\d+)\):(?<seeders>\d+) \/ :(?<leechers>\d+)$", RegexOptions.Compiled);
|
||||
private readonly HashSet<string> _hdResolutions = new () { "1080p", "1080i", "720p" };
|
||||
|
||||
public ShazbatParser(ShazbatSettings settings, TimeSpan rateLimit, IIndexerHttpClient httpClient, Logger logger)
|
||||
public ShazbatParser(ProviderDefinition definition, ShazbatSettings settings, TimeSpan rateLimit, IIndexerHttpClient httpClient, Logger logger)
|
||||
{
|
||||
_definition = definition;
|
||||
_settings = settings;
|
||||
_rateLimit = rateLimit;
|
||||
_httpClient = httpClient;
|
||||
@@ -272,7 +275,7 @@ public class ShazbatParser : IParseIndexerResponse
|
||||
_logger.Debug("Downloading Feed " + showRequest.ToString());
|
||||
|
||||
var releaseRequest = new IndexerRequest(showRequest);
|
||||
var releaseResponse = new IndexerResponse(releaseRequest, _httpClient.Execute(releaseRequest.HttpRequest));
|
||||
var releaseResponse = new IndexerResponse(releaseRequest, _httpClient.ExecuteProxied(releaseRequest.HttpRequest, _definition));
|
||||
|
||||
if (releaseResponse.HttpResponse.Content.ContainsIgnoreCase("sign in now"))
|
||||
{
|
||||
|
||||
@@ -1,287 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using AngleSharp.Html.Parser;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Indexers.Settings;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
[Obsolete("Remove per Site Request Prowlarr Issue 573")]
|
||||
public class TVVault : TorrentIndexerBase<UserPassTorrentBaseSettings>
|
||||
{
|
||||
public override string Name => "TVVault";
|
||||
public override string[] IndexerUrls => new[] { "https://tv-vault.me/" };
|
||||
private string LoginUrl => Settings.BaseUrl + "login.php";
|
||||
public override string Description => "TV-Vault is a very unique tracker dedicated for old TV shows, TV movies and documentaries.";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
public override TimeSpan RateLimit => TimeSpan.FromSeconds(5);
|
||||
|
||||
public TVVault(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new TVVaultRequestGenerator { Settings = Settings, Capabilities = Capabilities };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new TVVaultParser(Settings, Capabilities.Categories);
|
||||
}
|
||||
|
||||
protected override async Task DoLogin()
|
||||
{
|
||||
var requestBuilder = new HttpRequestBuilder(LoginUrl)
|
||||
{
|
||||
LogResponseContent = true,
|
||||
AllowAutoRedirect = true,
|
||||
Method = HttpMethod.Post
|
||||
};
|
||||
|
||||
var cookies = Cookies;
|
||||
Cookies = null;
|
||||
|
||||
var authLoginRequest = requestBuilder
|
||||
.AddFormParameter("username", Settings.Username)
|
||||
.AddFormParameter("password", Settings.Password)
|
||||
.AddFormParameter("keeplogged", "1")
|
||||
.AddFormParameter("login", "Log+In!")
|
||||
.SetHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||
.SetHeader("Referer", LoginUrl)
|
||||
.Build();
|
||||
|
||||
var response = await ExecuteAuth(authLoginRequest);
|
||||
|
||||
if (CheckIfLoginNeeded(response))
|
||||
{
|
||||
var parser = new HtmlParser();
|
||||
using var dom = parser.ParseDocument(response.Content);
|
||||
var errorMessage = dom.QuerySelector("form#loginform")?.TextContent.Trim();
|
||||
|
||||
throw new IndexerAuthException(errorMessage ?? "Unknown error message, please report.");
|
||||
}
|
||||
|
||||
cookies = response.GetCookies();
|
||||
UpdateCookies(cookies, DateTime.Now.AddDays(30));
|
||||
|
||||
_logger.Debug("TVVault authentication succeeded.");
|
||||
}
|
||||
|
||||
protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
|
||||
{
|
||||
return !httpResponse.Content.Contains("logout.php");
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
Flags = new List<IndexerFlag>
|
||||
{
|
||||
IndexerFlag.FreeLeech
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.TV);
|
||||
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.Movies);
|
||||
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.TVHD);
|
||||
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.TVSD);
|
||||
|
||||
return caps;
|
||||
}
|
||||
}
|
||||
|
||||
public class TVVaultRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
public UserPassTorrentBaseSettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories, string imdbId = null)
|
||||
{
|
||||
var searchUrl = string.Format("{0}/torrents.php", Settings.BaseUrl.TrimEnd('/'));
|
||||
|
||||
var qc = new NameValueCollection
|
||||
{
|
||||
{ "order_by", "s3" },
|
||||
{ "order_way", "DESC" },
|
||||
{ "disablegrouping", "1" }
|
||||
};
|
||||
|
||||
if (imdbId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
qc.Add("action", "advanced");
|
||||
qc.Add("imdbid", imdbId);
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(term))
|
||||
{
|
||||
qc.Add("searchstr", StripSearchString(term));
|
||||
}
|
||||
|
||||
var catList = Capabilities.Categories.MapTorznabCapsToTrackers(categories);
|
||||
|
||||
foreach (var cat in catList)
|
||||
{
|
||||
qc.Add($"filter_cat[{cat}]", "1");
|
||||
}
|
||||
|
||||
searchUrl = searchUrl + "?" + qc.GetQueryString();
|
||||
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Html);
|
||||
|
||||
yield return request;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
||||
{
|
||||
return new IndexerPageableRequestChain();
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
||||
{
|
||||
return new IndexerPageableRequestChain();
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
private string StripSearchString(string term)
|
||||
{
|
||||
// Search does not support searching with episode numbers so strip it if we have one
|
||||
// AND filter the result later to achieve the proper result
|
||||
term = Regex.Replace(term, @"[S|E]\d\d", string.Empty);
|
||||
return term.Trim();
|
||||
}
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
public class TVVaultParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly UserPassTorrentBaseSettings _settings;
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
|
||||
public TVVaultParser(UserPassTorrentBaseSettings settings, IndexerCapabilitiesCategories categories)
|
||||
{
|
||||
_settings = settings;
|
||||
_categories = categories;
|
||||
}
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var torrentInfos = new List<ReleaseInfo>();
|
||||
|
||||
var parser = new HtmlParser();
|
||||
using var doc = parser.ParseDocument(indexerResponse.Content);
|
||||
|
||||
// get params to build download link (user could be banned without those params)
|
||||
var rssFeedUri = new Uri(_settings.BaseUrl + doc.QuerySelector("link[href^=\"/feeds.php?feed=\"]")
|
||||
.GetAttribute("href"));
|
||||
var rssFeedQuery = HttpUtility.ParseQueryString(rssFeedUri.Query);
|
||||
var downloadLinkExtraParams = "&authkey=" + rssFeedQuery["authkey"] + "&torrent_pass=" + rssFeedQuery["passkey"];
|
||||
|
||||
var rows = doc.QuerySelectorAll("table.torrent_table > tbody > tr.torrent");
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var qDetailsLink = row.QuerySelector("a[href^=\"torrents.php?id=\"]");
|
||||
var title = qDetailsLink.TextContent;
|
||||
|
||||
var description = qDetailsLink.NextSibling.TextContent.Trim();
|
||||
title += " " + description;
|
||||
var details = _settings.BaseUrl + qDetailsLink.GetAttribute("href");
|
||||
var torrentId = qDetailsLink.GetAttribute("href").Split('=').Last();
|
||||
var link = _settings.BaseUrl + "torrents.php?action=download&id=" + torrentId + downloadLinkExtraParams;
|
||||
|
||||
var files = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(3)").TextContent);
|
||||
var publishDate = DateTimeUtil.FromTimeAgo(row.QuerySelector("td:nth-child(4)").TextContent);
|
||||
var size = ParseUtil.GetBytes(row.QuerySelector("td:nth-child(5)").FirstChild.TextContent);
|
||||
var grabs = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(6)").TextContent);
|
||||
var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(7)").TextContent);
|
||||
var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(8)").TextContent);
|
||||
|
||||
var dlVolumeFactor = row.QuerySelector("strong.freeleech_normal") != null ? 0 : 1;
|
||||
|
||||
var category = new List<IndexerCategory> { TvCategoryFromQualityParser.ParseTvShowQuality(description) };
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 0,
|
||||
Description = description,
|
||||
Title = title,
|
||||
PublishDate = publishDate,
|
||||
Categories = category,
|
||||
DownloadUrl = link,
|
||||
InfoUrl = details,
|
||||
Guid = link,
|
||||
Seeders = seeders,
|
||||
Peers = leechers + seeders,
|
||||
Size = size,
|
||||
Grabs = grabs,
|
||||
Files = files,
|
||||
DownloadVolumeFactor = dlVolumeFactor,
|
||||
UploadVolumeFactor = 1
|
||||
};
|
||||
|
||||
torrentInfos.Add(release);
|
||||
}
|
||||
|
||||
return torrentInfos.ToArray();
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ namespace NzbDrone.Core.Indexers.Definitions.TorrentRss
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return _torrentRssParserFactory.GetParser(Settings);
|
||||
return _torrentRssParserFactory.GetParser(Settings, Definition);
|
||||
}
|
||||
|
||||
public override IEnumerable<ProviderDefinition> DefaultDefinitions
|
||||
|
||||
@@ -3,12 +3,13 @@ using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions.TorrentRss
|
||||
{
|
||||
public interface ITorrentRssParserFactory
|
||||
{
|
||||
TorrentRssParser GetParser(TorrentRssIndexerSettings settings);
|
||||
TorrentRssParser GetParser(TorrentRssIndexerSettings settings, ProviderDefinition definition);
|
||||
}
|
||||
|
||||
public class TorrentRssParserFactory : ITorrentRssParserFactory
|
||||
@@ -26,10 +27,10 @@ namespace NzbDrone.Core.Indexers.Definitions.TorrentRss
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public TorrentRssParser GetParser(TorrentRssIndexerSettings indexerSettings)
|
||||
public TorrentRssParser GetParser(TorrentRssIndexerSettings indexerSettings, ProviderDefinition definition)
|
||||
{
|
||||
var key = indexerSettings.ToJson();
|
||||
var parserSettings = _settingsCache.Get(key, () => DetectParserSettings(indexerSettings), TimeSpan.FromDays(7));
|
||||
var parserSettings = _settingsCache.Get(key, () => DetectParserSettings(indexerSettings, definition), TimeSpan.FromDays(7));
|
||||
|
||||
if (parserSettings.UseEZTVFormat)
|
||||
{
|
||||
@@ -51,9 +52,9 @@ namespace NzbDrone.Core.Indexers.Definitions.TorrentRss
|
||||
};
|
||||
}
|
||||
|
||||
private TorrentRssIndexerParserSettings DetectParserSettings(TorrentRssIndexerSettings indexerSettings)
|
||||
private TorrentRssIndexerParserSettings DetectParserSettings(TorrentRssIndexerSettings indexerSettings, ProviderDefinition definition)
|
||||
{
|
||||
var settings = _torrentRssSettingsDetector.Detect(indexerSettings);
|
||||
var settings = _torrentRssSettingsDetector.Detect(indexerSettings, definition);
|
||||
|
||||
if (settings == null)
|
||||
{
|
||||
|
||||
@@ -9,28 +9,29 @@ using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions.TorrentRss
|
||||
{
|
||||
public interface ITorrentRssSettingsDetector
|
||||
{
|
||||
TorrentRssIndexerParserSettings Detect(TorrentRssIndexerSettings settings);
|
||||
TorrentRssIndexerParserSettings Detect(TorrentRssIndexerSettings settings, ProviderDefinition definition);
|
||||
}
|
||||
|
||||
public class TorrentRssSettingsDetector : ITorrentRssSettingsDetector
|
||||
{
|
||||
private const long ValidSizeThreshold = 2 * 1024 * 1024;
|
||||
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IIndexerHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public TorrentRssSettingsDetector(IHttpClient httpClient, Logger logger)
|
||||
public TorrentRssSettingsDetector(IIndexerHttpClient httpClient, Logger logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public TorrentRssIndexerParserSettings Detect(TorrentRssIndexerSettings settings)
|
||||
public TorrentRssIndexerParserSettings Detect(TorrentRssIndexerSettings settings, ProviderDefinition definition)
|
||||
{
|
||||
_logger.Debug("Evaluating TorrentRss feed '{0}'", settings.BaseUrl);
|
||||
|
||||
@@ -43,7 +44,7 @@ namespace NzbDrone.Core.Indexers.Definitions.TorrentRss
|
||||
|
||||
try
|
||||
{
|
||||
httpResponse = _httpClient.Execute(request.HttpRequest);
|
||||
httpResponse = _httpClient.ExecuteProxied(request.HttpRequest, definition);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -19,7 +19,8 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public class TorrentsCSV : TorrentIndexerBase<NoAuthTorrentBaseSettings>
|
||||
{
|
||||
public override string Name => "TorrentsCSV";
|
||||
public override string[] IndexerUrls => new[] { "https://torrents-csv.ml/" };
|
||||
public override string[] IndexerUrls => new[] { "https://torrents-csv.com/" };
|
||||
public override string[] LegacyUrls => new[] { "https://torrents-csv.ml/" };
|
||||
public override string Language => "en-US";
|
||||
public override string Description => "Torrents.csv is a self-hostable open source torrent search engine and database";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Http.Dispatchers;
|
||||
using NzbDrone.Common.TPL;
|
||||
@@ -34,56 +36,63 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
public async Task<HttpResponse> ExecuteProxiedAsync(HttpRequest request, ProviderDefinition definition)
|
||||
{
|
||||
var selectedProxy = GetProxy(definition);
|
||||
var selectedProxies = GetProxies(definition);
|
||||
|
||||
request = PreRequest(request, selectedProxy);
|
||||
request = PreRequest(request, selectedProxies);
|
||||
|
||||
return PostResponse(await ExecuteAsync(request), selectedProxy);
|
||||
return PostResponse(await ExecuteAsync(request), selectedProxies);
|
||||
}
|
||||
|
||||
public HttpResponse ExecuteProxied(HttpRequest request, ProviderDefinition definition)
|
||||
{
|
||||
var selectedProxy = GetProxy(definition);
|
||||
var selectedProxies = GetProxies(definition);
|
||||
|
||||
request = PreRequest(request, selectedProxy);
|
||||
request = PreRequest(request, selectedProxies);
|
||||
|
||||
return PostResponse(Execute(request), selectedProxy);
|
||||
return PostResponse(Execute(request), selectedProxies);
|
||||
}
|
||||
|
||||
private IIndexerProxy GetProxy(ProviderDefinition definition)
|
||||
private IList<IIndexerProxy> GetProxies(ProviderDefinition definition)
|
||||
{
|
||||
// Skip DB call if no tags on the indexers
|
||||
if (definition is { Id: > 0 } && definition.Tags.Count == 0)
|
||||
{
|
||||
return null;
|
||||
return Array.Empty<IIndexerProxy>();
|
||||
}
|
||||
|
||||
var proxies = _indexerProxyFactory.GetAvailableProviders();
|
||||
var selectedProxy = proxies.FirstOrDefault(proxy => definition.Tags.Intersect(proxy.Definition.Tags).Any());
|
||||
|
||||
if (selectedProxy == null && definition is not { Id: not 0 })
|
||||
var selectedProxies = proxies
|
||||
.Where(proxy => definition.Tags.Intersect(proxy.Definition.Tags).Any())
|
||||
.GroupBy(p => p is FlareSolverr)
|
||||
.Select(g => g.First())
|
||||
.OrderBy(p => p is FlareSolverr)
|
||||
.ToList();
|
||||
|
||||
if (!selectedProxies.Any() && definition is not { Id: not 0 })
|
||||
{
|
||||
selectedProxy = proxies.FirstOrDefault(p => p is FlareSolverr);
|
||||
selectedProxies = new List<IIndexerProxy>();
|
||||
selectedProxies.AddIfNotNull(proxies.Find(p => p is FlareSolverr));
|
||||
}
|
||||
|
||||
return selectedProxy;
|
||||
return selectedProxies;
|
||||
}
|
||||
|
||||
private HttpRequest PreRequest(HttpRequest request, IIndexerProxy selectedProxy)
|
||||
private HttpRequest PreRequest(HttpRequest request, IList<IIndexerProxy> selectedProxies)
|
||||
{
|
||||
if (selectedProxy != null)
|
||||
if (selectedProxies != null && selectedProxies.Any())
|
||||
{
|
||||
request = selectedProxy.PreRequest(request);
|
||||
request = selectedProxies.Aggregate(request, (current, selectedProxy) => selectedProxy.PreRequest(current));
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private HttpResponse PostResponse(HttpResponse response, IIndexerProxy selectedProxy)
|
||||
private HttpResponse PostResponse(HttpResponse response, IList<IIndexerProxy> selectedProxies)
|
||||
{
|
||||
if (selectedProxy != null)
|
||||
if (selectedProxies != null && selectedProxies.Any())
|
||||
{
|
||||
response = selectedProxy.PostResponse(response);
|
||||
response = selectedProxies.Aggregate(response, (current, selectedProxy) => selectedProxy.PostResponse(current));
|
||||
}
|
||||
|
||||
return response;
|
||||
|
||||
@@ -63,5 +63,8 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
[FieldDefinition(4, Type = FieldType.Number, Label = "IndexerSettingsPackSeedTime", HelpText = "IndexerSettingsPackSeedTimeIndexerHelpText", Unit = "minutes", Advanced = true)]
|
||||
public int? PackSeedTime { get; set; }
|
||||
|
||||
[FieldDefinition(5, Type = FieldType.Checkbox, Label = "IndexerSettingsRejectBlocklistedTorrentHashes", HelpText = "IndexerSettingsRejectBlocklistedTorrentHashesHelpText", Advanced = true)]
|
||||
public bool RejectBlocklistedTorrentHashesWhileGrabbing { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
"BeforeUpdate": "Before update",
|
||||
"BindAddress": "Bind Address",
|
||||
"BindAddressHelpText": "Valid IP address, localhost or '*' for all interfaces",
|
||||
"BlackholeFolderHelpText": "Folder in which {appName} will store the {extension} file",
|
||||
"Book": "Book",
|
||||
"BookSearch": "Book Search",
|
||||
"BookSearchTypes": "Book Search Types",
|
||||
@@ -133,11 +134,13 @@
|
||||
"CountIndexersAvailable": "{count} indexer(s) available",
|
||||
"CountIndexersSelected": "{count} indexer(s) selected",
|
||||
"Custom": "Custom",
|
||||
"CustomFilter": "Custom Filter",
|
||||
"CustomFilters": "Custom Filters",
|
||||
"Database": "Database",
|
||||
"DatabaseMigration": "Database Migration",
|
||||
"Date": "Date",
|
||||
"Dates": "Dates",
|
||||
"DefaultCategory": "Default Category",
|
||||
"DefaultNameCopiedProfile": "{name} - Copy",
|
||||
"Delete": "Delete",
|
||||
"DeleteAppProfile": "Delete App Profile",
|
||||
@@ -163,8 +166,10 @@
|
||||
"DeleteTag": "Delete Tag",
|
||||
"DeleteTagMessageText": "Are you sure you want to delete the tag '{label}'?",
|
||||
"Description": "Description",
|
||||
"Destination": "Destination",
|
||||
"Details": "Details",
|
||||
"DevelopmentSettings": "Development Settings",
|
||||
"Directory": "Directory",
|
||||
"Disabled": "Disabled",
|
||||
"DisabledForLocalAddresses": "Disabled for Local Addresses",
|
||||
"DisabledUntil": "Disabled Until",
|
||||
@@ -174,12 +179,53 @@
|
||||
"DownloadClient": "Download Client",
|
||||
"DownloadClientAriaSettingsDirectoryHelpText": "Optional location to put downloads in, leave blank to use the default Aria2 location",
|
||||
"DownloadClientCategory": "Download Client Category",
|
||||
"DownloadClientDelugeSettingsUrlBaseHelpText": "Adds a prefix to the deluge json url, see {url}",
|
||||
"DownloadClientDownloadStationSettingsDirectoryHelpText": "Optional shared folder to put downloads into, leave blank to use the default Download Station location",
|
||||
"DownloadClientFloodSettingsAdditionalTags": "Additional Tags",
|
||||
"DownloadClientFloodSettingsAdditionalTagsHelpText": "Adds properties of media as tags. Hints are examples.",
|
||||
"DownloadClientFloodSettingsTagsHelpText": "Initial tags of a download. To be recognized, a download must have all initial tags. This avoids conflicts with unrelated downloads.",
|
||||
"DownloadClientFloodSettingsUrlBaseHelpText": "Adds a prefix to the Flood API, such as {url}",
|
||||
"DownloadClientFreeboxSettingsApiUrl": "API URL",
|
||||
"DownloadClientFreeboxSettingsApiUrlHelpText": "Define Freebox API base URL with API version, eg '{url}', defaults to '{defaultApiUrl}'",
|
||||
"DownloadClientFreeboxSettingsAppId": "App ID",
|
||||
"DownloadClientFreeboxSettingsAppIdHelpText": "App ID given when creating access to Freebox API (ie 'app_id')",
|
||||
"DownloadClientFreeboxSettingsAppToken": "App Token",
|
||||
"DownloadClientFreeboxSettingsAppTokenHelpText": "App token retrieved when creating access to Freebox API (ie 'app_token')",
|
||||
"DownloadClientFreeboxSettingsHostHelpText": "Hostname or host IP address of the Freebox, defaults to '{url}' (will only work if on same network)",
|
||||
"DownloadClientFreeboxSettingsPortHelpText": "Port used to access Freebox interface, defaults to '{port}'",
|
||||
"DownloadClientNzbgetSettingsAddPausedHelpText": "This option requires at least NzbGet version 16.0",
|
||||
"DownloadClientPneumaticSettingsNzbFolder": "Nzb Folder",
|
||||
"DownloadClientPneumaticSettingsNzbFolderHelpText": "This folder will need to be reachable from XBMC",
|
||||
"DownloadClientPneumaticSettingsStrmFolder": "Strm Folder",
|
||||
"DownloadClientPneumaticSettingsStrmFolderHelpText": ".strm files in this folder will be import by drone",
|
||||
"DownloadClientPriorityHelpText": "Prioritize multiple Download Clients. Round-Robin is used for clients with the same priority.",
|
||||
"DownloadClientQbittorrentSettingsContentLayout": "Content Layout",
|
||||
"DownloadClientQbittorrentSettingsContentLayoutHelpText": "Whether to use qBittorrent's configured content layout, the original layout from the torrent or always create a subfolder (qBittorrent 4.3.2+)",
|
||||
"DownloadClientQbittorrentSettingsFirstAndLastFirst": "First and Last First",
|
||||
"DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText": "Download first and last pieces first (qBittorrent 4.1.0+)",
|
||||
"DownloadClientQbittorrentSettingsInitialStateHelpText": "Initial state for torrents added to qBittorrent. Note that Forced Torrents do not abide by seed restrictions",
|
||||
"DownloadClientQbittorrentSettingsSequentialOrder": "Sequential Order",
|
||||
"DownloadClientQbittorrentSettingsSequentialOrderHelpText": "Download in sequential order (qBittorrent 4.1.0+)",
|
||||
"DownloadClientQbittorrentSettingsUseSslHelpText": "Use a secure connection. See Options -> Web UI -> 'Use HTTPS instead of HTTP' in qBittorrent.",
|
||||
"DownloadClientRTorrentSettingsAddStopped": "Add Stopped",
|
||||
"DownloadClientRTorrentSettingsAddStoppedHelpText": "Enabling will add torrents and magnets to rTorrent in a stopped state. This may break magnet files.",
|
||||
"DownloadClientRTorrentSettingsDirectoryHelpText": "Optional location to put downloads in, leave blank to use the default rTorrent location",
|
||||
"DownloadClientRTorrentSettingsUrlPath": "Url Path",
|
||||
"DownloadClientRTorrentSettingsUrlPathHelpText": "Path to the XMLRPC endpoint, see {url}. This is usually RPC2 or [path to ruTorrent]{url2} when using ruTorrent.",
|
||||
"DownloadClientSettings": "Download Client Settings",
|
||||
"DownloadClientSettingsAddPaused": "Add Paused",
|
||||
"DownloadClientSettingsDefaultCategoryHelpText": "Default fallback category if no mapped category exists for a release. Adding a category specific to {appName} avoids conflicts with unrelated non-{appName} downloads. Using a category is optional, but strongly recommended.",
|
||||
"DownloadClientSettingsDefaultCategorySubFolderHelpText": "Default fallback category if no mapped category exists for a release. Adding a category specific to {appName} avoids conflicts with unrelated non-{appName} downloads. Using a category is optional, but strongly recommended. Creates a [category] subdirectory in the output directory.",
|
||||
"DownloadClientSettingsDestinationHelpText": "Manually specifies download destination, leave blank to use the default",
|
||||
"DownloadClientSettingsInitialState": "Initial State",
|
||||
"DownloadClientSettingsInitialStateHelpText": "Initial state for torrents added to {clientName}",
|
||||
"DownloadClientSettingsPriorityItemHelpText": "Priority to use when grabbing items",
|
||||
"DownloadClientSettingsUrlBaseHelpText": "Adds a prefix to the {clientName} url, such as {url}",
|
||||
"DownloadClientSettingsUseSslHelpText": "Use secure connection when connection to {clientName}",
|
||||
"DownloadClientStatusCheckAllClientMessage": "All download clients are unavailable due to failures",
|
||||
"DownloadClientStatusCheckSingleClientMessage": "Download clients unavailable due to failures: {0}",
|
||||
"DownloadClientTransmissionSettingsDirectoryHelpText": "Optional location to put downloads in, leave blank to use the default Transmission location",
|
||||
"DownloadClientTransmissionSettingsUrlBaseHelpText": "Adds a prefix to the {clientName} rpc url, eg {url}, defaults to '{defaultUrl}'",
|
||||
"DownloadClients": "Download Clients",
|
||||
"DownloadClientsLoadError": "Unable to load download clients",
|
||||
"DownloadClientsSettingsSummary": "Download clients configuration for integration into {appName} UI search",
|
||||
@@ -349,6 +395,8 @@
|
||||
"IndexerSettingsPasskey": "Pass Key",
|
||||
"IndexerSettingsQueryLimit": "Query Limit",
|
||||
"IndexerSettingsQueryLimitHelpText": "The number of max queries as specified by the respective unit that {appName} will allow to the site",
|
||||
"IndexerSettingsRejectBlocklistedTorrentHashes": "Reject Blocklisted Torrent Hashes While Grabbing",
|
||||
"IndexerSettingsRejectBlocklistedTorrentHashesHelpText": "If a torrent is blocked by hash it may not properly be rejected during RSS/Search for some indexers, enabling this will allow it to be rejected after the torrent is grabbed, but before it is sent to the client.",
|
||||
"IndexerSettingsRssKey": "RSS Key",
|
||||
"IndexerSettingsSeedRatio": "Seed Ratio",
|
||||
"IndexerSettingsSeedRatioHelpText": "The ratio a torrent should reach before stopping, empty uses the download client's default. Ratio should be at least 1.0 and follow the indexers rules",
|
||||
@@ -375,6 +423,7 @@
|
||||
"InvalidUILanguage": "Your UI is set to an invalid language, correct it and save your settings",
|
||||
"KeyboardShortcuts": "Keyboard Shortcuts",
|
||||
"Label": "Label",
|
||||
"LabelIsRequired": "Label is required",
|
||||
"Language": "Language",
|
||||
"LastDuration": "Last Duration",
|
||||
"LastExecution": "Last Execution",
|
||||
@@ -542,6 +591,7 @@
|
||||
"SearchType": "Search Type",
|
||||
"SearchTypes": "Search Types",
|
||||
"Season": "Season",
|
||||
"SecretToken": "Secret Token",
|
||||
"Security": "Security",
|
||||
"SeedRatio": "Seed Ratio",
|
||||
"SeedRatioHelpText": "The ratio a torrent should reach before stopping, empty is app's default",
|
||||
@@ -616,6 +666,11 @@
|
||||
"Today": "Today",
|
||||
"Tomorrow": "Tomorrow",
|
||||
"Torrent": "Torrent",
|
||||
"TorrentBlackholeSaveMagnetFiles": "Save Magnet Files",
|
||||
"TorrentBlackholeSaveMagnetFilesExtension": "Save Magnet Files Extension",
|
||||
"TorrentBlackholeSaveMagnetFilesExtensionHelpText": "Extension to use for magnet links, defaults to '.magnet'",
|
||||
"TorrentBlackholeSaveMagnetFilesHelpText": "Save the magnet link if no .torrent file is available (only useful if the download client supports magnets saved to a file)",
|
||||
"TorrentBlackholeTorrentFolder": "Torrent Folder",
|
||||
"Torrents": "Torrents",
|
||||
"TorznabUrl": "Torznab Url",
|
||||
"TotalGrabs": "Total Grabs",
|
||||
@@ -666,7 +721,9 @@
|
||||
"Url": "Url",
|
||||
"UrlBaseHelpText": "For reverse proxy support, default is empty",
|
||||
"UseProxy": "Use Proxy",
|
||||
"UseSsl": "Use SSL",
|
||||
"Usenet": "Usenet",
|
||||
"UsenetBlackholeNzbFolder": "Nzb Folder",
|
||||
"UserAgentProvidedByTheAppThatCalledTheAPI": "User-Agent provided by the app that called the API",
|
||||
"Username": "Username",
|
||||
"Version": "Version",
|
||||
@@ -676,6 +733,7 @@
|
||||
"Website": "Website",
|
||||
"WhatsNew": "What's New?",
|
||||
"Wiki": "Wiki",
|
||||
"XmlRpcPath": "XML RPC Path",
|
||||
"Year": "Year",
|
||||
"Yes": "Yes",
|
||||
"YesCancel": "Yes, Cancel",
|
||||
|
||||
@@ -56,9 +56,9 @@
|
||||
"ProxyCheckBadRequestMessage": "Fallo al comprobar el proxy. Status code: {0}",
|
||||
"Proxy": "Proxy",
|
||||
"Options": "Opciones",
|
||||
"NoChange": "Sin Cambio",
|
||||
"NoChanges": "Sin Cambios",
|
||||
"MoreInfo": "Más Información",
|
||||
"NoChange": "Sin cambio",
|
||||
"NoChanges": "Sin cambios",
|
||||
"MoreInfo": "Más información",
|
||||
"Logging": "Registro de eventos",
|
||||
"LogFiles": "Archivos de Registro",
|
||||
"Language": "Idioma",
|
||||
@@ -203,8 +203,8 @@
|
||||
"Password": "Contraseña",
|
||||
"PageSizeHelpText": "Número de elementos por página",
|
||||
"PackageVersion": "Versión del paquete",
|
||||
"NotificationTriggers": "Desencadenantes de Notificaciones",
|
||||
"NoLeaveIt": "No, Déjalo",
|
||||
"NotificationTriggers": "Disparadores de notificación",
|
||||
"NoLeaveIt": "No, déjalo",
|
||||
"New": "Nuevo",
|
||||
"NetCore": ".NET Core",
|
||||
"Mode": "Modo",
|
||||
@@ -214,7 +214,7 @@
|
||||
"LogLevel": "Nivel de Registro",
|
||||
"LaunchBrowserHelpText": " Abrir un navegador web e ir a la página de inicio de {appName} al arrancar la app.",
|
||||
"Interval": "Intervalo",
|
||||
"IndexerFlags": "Marcas de Indexer",
|
||||
"IndexerFlags": "Banderas del indexador",
|
||||
"IncludeHealthWarningsHelpText": "Incluir Alertas de Salud",
|
||||
"IllRestartLater": "Lo reiniciaré más tarde",
|
||||
"IgnoredAddresses": "Ignorar direcciones",
|
||||
@@ -258,12 +258,12 @@
|
||||
"ProwlarrSupportsAnyIndexer": "{appName} soporta cualquier indexer que utilice el estandar Newznab, como también cualquiera de los indexers listados debajo.",
|
||||
"ProwlarrSupportsAnyDownloadClient": "{appName} soporta cualquier gestor de descargas indicado debajo.",
|
||||
"NoUpdatesAreAvailable": "No hay actualizaciones disponibles",
|
||||
"NoTagsHaveBeenAddedYet": "No se han añadido etiquetas todavía",
|
||||
"NoLogFiles": "Sin archivos de registro",
|
||||
"NoTagsHaveBeenAddedYet": "Ninguna etiqueta ha sido añadida aún",
|
||||
"NoLogFiles": "No hay archivos de registro",
|
||||
"NoBackupsAreAvailable": "No hay copias de seguridad disponibles",
|
||||
"MaintenanceRelease": "Lanzamiento de mantenimiento: Corrección de errores y otras mejoras. Ver historial de commits de Github para mas detalle",
|
||||
"ForMoreInformationOnTheIndividualDownloadClients": "Para más información individual de los gestores de descarga, haz clic en lls botones de información.",
|
||||
"FilterPlaceHolder": "Buscar películas",
|
||||
"FilterPlaceHolder": "Buscar Indexadores",
|
||||
"Exception": "Excepción",
|
||||
"ErrorLoadingContents": "Error cargando contenidos",
|
||||
"UILanguageHelpTextWarning": "Recargar el Navegador",
|
||||
@@ -293,7 +293,7 @@
|
||||
"Add": "Añadir",
|
||||
"Custom": "Personalizado",
|
||||
"Donations": "Donaciones",
|
||||
"SearchIndexers": "Buscar películas",
|
||||
"SearchIndexers": "Buscar Indexadores",
|
||||
"Enabled": "Habilitado",
|
||||
"Grabs": "Capturar",
|
||||
"Presets": "Preajustes",
|
||||
@@ -323,7 +323,7 @@
|
||||
"OnGrab": "Al Capturar lanzamiento",
|
||||
"OnHealthIssue": "En Problema de Salud",
|
||||
"TestAllIndexers": "Comprobar Todos los Indexers",
|
||||
"NotificationTriggersHelpText": "Seleccione qué eventos deben activar esta notificación",
|
||||
"NotificationTriggersHelpText": "Selecciona qué eventos deberían disparar esta notificación",
|
||||
"OnApplicationUpdate": "Al Actualizar La Aplicación",
|
||||
"OnApplicationUpdateHelpText": "Al Actualizar La Aplicación",
|
||||
"AddRemoveOnly": "Sólo añadir y eliminar",
|
||||
@@ -378,7 +378,7 @@
|
||||
"DeleteSelectedIndexersMessageText": "¿Está seguro de querer eliminar {count} indexador(es) seleccionado(s)?",
|
||||
"DeleteSelectedDownloadClientsMessageText": "¿Está seguro de querer eliminar {count} cliente(s) de descarga seleccionado(s)?",
|
||||
"ApplyTagsHelpTextHowToApplyApplications": "Cómo aplicar etiquetas a las aplicaciones seleccionadas",
|
||||
"SelectIndexers": "Buscar películas",
|
||||
"SelectIndexers": "Seleccionar Indexadores",
|
||||
"ApplyTagsHelpTextHowToApplyIndexers": "Cómo aplicar etiquetas a los indexadores seleccionados",
|
||||
"ApplyTagsHelpTextRemove": "Eliminar: Elimina las etiquetas introducidas",
|
||||
"ApplyTagsHelpTextReplace": "Reemplazar: Sustituye las etiquetas por las introducidas (introduce \"no tags\" para borrar todas las etiquetas)",
|
||||
@@ -411,7 +411,7 @@
|
||||
"Artist": "Artista",
|
||||
"DeleteAppProfileMessageText": "¿Estás seguro de que quieres eliminar el perfil de la aplicación '{name}'?",
|
||||
"AddConnection": "Añadir Conexión",
|
||||
"NotificationStatusAllClientHealthCheckMessage": "Las listas no están disponibles debido a errores",
|
||||
"NotificationStatusAllClientHealthCheckMessage": "Las notificaciones no están disponibles debido a fallos",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Listas no disponibles debido a errores: {0}",
|
||||
"EditIndexerImplementation": "Editar Indexador - {implementationName}",
|
||||
"AuthBasic": "Básico (ventana emergente del navegador)",
|
||||
@@ -421,7 +421,7 @@
|
||||
"Clone": "Clonar",
|
||||
"DisabledForLocalAddresses": "Deshabilitado para Direcciones Locales",
|
||||
"External": "Externo",
|
||||
"None": "Ninguna",
|
||||
"None": "Ninguno",
|
||||
"ResetAPIKeyMessageText": "¿Está seguro de que desea restablecer su clave API?",
|
||||
"EditIndexerProxyImplementation": "Editar Proxy de Indexador - { implementationName}",
|
||||
"AppUpdated": "{appName} Actualizado",
|
||||
@@ -549,7 +549,7 @@
|
||||
"QueryOptions": "Opciones de Consulta",
|
||||
"NewznabUrl": "Url Newznab",
|
||||
"QueryType": "Tipo de Consulta",
|
||||
"SearchCountIndexers": "Buscar {count} indexador(es)",
|
||||
"SearchCountIndexers": "Buscar en {count} indexador(es)",
|
||||
"SelectedCountOfCountReleases": "Seleccionados {selectedCount} de {itemCount} lanzamientos",
|
||||
"SyncLevel": "Nivel de Sincronización",
|
||||
"StopSelecting": "Detener la Selección",
|
||||
@@ -611,5 +611,8 @@
|
||||
"TotalIndexerSuccessfulGrabs": "Capturas con Éxito Totales por Indexador",
|
||||
"NotificationsEmailSettingsUseEncryptionHelpText": "Si prefiere utilizar el cifrado si está configurado en el servidor, utilizar siempre el cifrado mediante SSL (sólo puerto 465) o StartTLS (cualquier otro puerto) o no utilizar nunca el cifrado",
|
||||
"IndexerHDBitsSettingsPasskeyHelpText": "Clave de acceso desde los Detalles de Usuario",
|
||||
"IndexerSettingsPasskey": "Clave de Acceso"
|
||||
"IndexerSettingsPasskey": "Clave de Acceso",
|
||||
"BlackholeFolderHelpText": "La carpeta en donde {appName} se almacenaran los {extension} file",
|
||||
"CustomFilter": "Filtros personalizados",
|
||||
"LabelIsRequired": "Se requiere etiqueta"
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"AddingTag": "Címke hozzáadása",
|
||||
"Error": "Hiba",
|
||||
"DeleteTag": "Címke törlése",
|
||||
"EnableAutomaticSearchHelpText": "Akkor kerül felhasználásra, ha az automatikus kereséseket a kezelőfelületen vagy a {appName}-on keresztül hajtják végre",
|
||||
"EnableAutomaticSearchHelpText": "Akkor lesz használatos, ha automatikus keresést hajt végre a felhasználói felületen vagy a(z) {appName} alkalmazáson keresztül",
|
||||
"EnableAutomaticSearch": "Engedélyezd az Automatikus Keresést",
|
||||
"Enable": "Aktiválás",
|
||||
"EditIndexer": "Indexer Szerkesztése",
|
||||
@@ -92,12 +92,12 @@
|
||||
"Failed": "Nem sikerült",
|
||||
"ExistingTag": "Létező címke",
|
||||
"Exception": "Kivétel",
|
||||
"EventType": "Események Típusa",
|
||||
"EventType": "Esemény típus",
|
||||
"Events": "Események",
|
||||
"ErrorLoadingContents": "Hiba történt a tartalom betöltésekor",
|
||||
"ErrorLoadingContents": "Hiba a tartalom betöltésekor",
|
||||
"EnableSslHelpText": " A hatálybalépéshez újra kell indítani rendszergazdaként",
|
||||
"EnableSSL": "SSL Engedélyezése",
|
||||
"EnableInteractiveSearchHelpText": "Interaktív keresés esetén használható",
|
||||
"EnableInteractiveSearchHelpText": "Interaktív keresés esetén lesz használatos",
|
||||
"EnableInteractiveSearch": "Interaktív Keresés Engedélyezése",
|
||||
"System": "Rendszer",
|
||||
"SuggestTranslationChange": "Javasolj fordítási változtatást",
|
||||
@@ -134,20 +134,20 @@
|
||||
"SaveSettings": "Beállítások mentése",
|
||||
"SaveChanges": "Változtatások mentése",
|
||||
"Save": "Mentés",
|
||||
"RssIsNotSupportedWithThisIndexer": "Az RSS nem támogatott ezzel az indexerrel",
|
||||
"RssIsNotSupportedWithThisIndexer": "Ez az indexelő nem támogatja az RSS-t",
|
||||
"Retention": "Visszatartás",
|
||||
"Result": "Eredmény",
|
||||
"RestoreBackup": "Biztonsági mentés visszaállítása",
|
||||
"Restore": "Visszaállít",
|
||||
"RestartRequiredHelpTextWarning": "Újraindítás szükséges a hatálybalépéshez",
|
||||
"RestartRequiredHelpTextWarning": "Újraindítás szükséges az életbe lépéshez",
|
||||
"RestartProwlarr": "{appName} Újraindítása",
|
||||
"RestartNow": "Újraindítás Most",
|
||||
"RestartNow": "Újraindítás most",
|
||||
"Restart": "Újrakezd",
|
||||
"ResetAPIKey": "API Kulcs visszaállítása",
|
||||
"ResetAPIKey": "API Kulcs Visszaállítása",
|
||||
"Reset": "Visszaállítás",
|
||||
"RemovingTag": "Címke eltávolítása",
|
||||
"RemoveFilter": "Szűrő törlése",
|
||||
"RemovedFromTaskQueue": "Eltávolítva a feladatsorról",
|
||||
"RemoveFilter": "Szűrő Eltávolítás",
|
||||
"RemovedFromTaskQueue": "Eltávolítva a feladatsorból",
|
||||
"Reload": "Újratölt",
|
||||
"ReleaseStatus": "Kiadás státusza",
|
||||
"ReleaseBranchCheckOfficialBranchMessage": "A(z) {0} nem érvényes {appName} frissítési ágazat, ezért nem kap frissítéseket",
|
||||
@@ -157,21 +157,21 @@
|
||||
"ProwlarrSupportsAnyIndexer": "A {appName} számos indexert támogat, minden olyan indexelő mellett, amely a Newznab / Torznab szabványt használja, valamint a 'Generic Newznab' (usenethez) vagy a 'Generic Torznab' (torrentekhez) használatával. Keresés és az alább felsorolt indexelők kiválasztása.",
|
||||
"ProwlarrSupportsAnyDownloadClient": "A {appName} minden olyan letöltési klienst támogat, amely a Newznab szabványt használja, valamint az alább felsorolt letöltési klienseket.",
|
||||
"Queue": "Várakozási sor",
|
||||
"ProxyUsernameHelpText": "Csak akkor kell megadnod felhasználónevet és jelszót, ha szükséges. Egyébként hagyd üresen.",
|
||||
"ProxyType": "Proxy Típusa",
|
||||
"ProxyPasswordHelpText": "Csak akkor kell megadnod felhasználónevet és jelszót, ha szükséges. Egyébként hagyd üresen.",
|
||||
"ProxyUsernameHelpText": "Csak akkor kell megadnia egy felhasználónevet és jelszót, ha szükséges. Ellenkező esetben hagyja üresen.",
|
||||
"ProxyType": "Proxy típus",
|
||||
"ProxyPasswordHelpText": "Csak akkor kell megadnia egy felhasználónevet és jelszót, ha szükséges. Ellenkező esetben hagyja üresen.",
|
||||
"ProxyCheckResolveIpMessage": "Nem sikerült megoldani a konfigurált proxykiszolgáló IP-címét {0}",
|
||||
"ProxyCheckFailedToTestMessage": "Proxy tesztelése sikertelen: {0}",
|
||||
"ProxyCheckBadRequestMessage": "Proxy tesztelése sikertelen. Állapotkód: {0}",
|
||||
"ProxyBypassFilterHelpText": "Használja elválasztóként a ',' és a '*' karaktereket, az aldomainek helyettesítőjeként",
|
||||
"ProxyBypassFilterHelpText": "Használja a ',' jelet elválasztóként és a '*' jelet. helyettesítő karakterként az aldomainekhez",
|
||||
"Proxy": "Proxy",
|
||||
"Protocol": "Protokoll",
|
||||
"Priority": "Prioritás",
|
||||
"PortNumber": "Port száma",
|
||||
"Port": "Port",
|
||||
"PendingChangesStayReview": "Maradj, és tekintsd át a változásokat",
|
||||
"PendingChangesMessage": "Nem mentett módosításaid vannak, biztosan el akarod hagyni ezt az oldalt?",
|
||||
"PendingChangesDiscardChanges": "Változtatások törlése és kilépés",
|
||||
"PendingChangesStayReview": "Maradjon és tekintse át a változtatásokat",
|
||||
"PendingChangesMessage": "Vannak nem mentett módosításai. Biztosan elhagyja ezt az oldalt?",
|
||||
"PendingChangesDiscardChanges": "Vesse el a változtatásokat, és lépjen ki",
|
||||
"Peers": "Peerek",
|
||||
"Password": "Jelszó",
|
||||
"PageSizeHelpText": "Az egyes oldalakon megjelenítendő elemek száma",
|
||||
@@ -320,7 +320,7 @@
|
||||
"AddNewIndexer": "Új indexelő hozzáadása",
|
||||
"AddedToDownloadClient": "Kiadás hozzáadva a klienshez",
|
||||
"EnableRssHelpText": "RSS Engedélyezése az Indexerekhez",
|
||||
"EnableRss": "RSS Engedélyezése",
|
||||
"EnableRss": "RSS Aktiválás",
|
||||
"Wiki": "Wiki",
|
||||
"Rss": "RSS",
|
||||
"RedirectHelpText": "Átirányítja a bejövő letöltési kérelmet az indexelő számára, és közvetlenül adja át a fájlt ahelyett, hogy a kérést a {appName}-en keresztül proxyba tenné",
|
||||
@@ -501,7 +501,7 @@
|
||||
"AuthForm": "Űrlapok (bejelentkezési oldal)",
|
||||
"DisabledForLocalAddresses": "Helyi címeknél letiltva",
|
||||
"None": "Egyik sem",
|
||||
"ResetAPIKeyMessageText": "Biztos hogy vissza szeretnéd állítani az API-Kulcsod?",
|
||||
"ResetAPIKeyMessageText": "Biztosan visszaállítja API-kulcsát?",
|
||||
"AuthenticationRequiredPasswordHelpTextWarning": "Adjon meg új jelszót",
|
||||
"AuthenticationRequiredUsernameHelpTextWarning": "Adjon meg új felhasználónevet",
|
||||
"AppUpdated": "{appName} frissítve",
|
||||
@@ -545,5 +545,8 @@
|
||||
"days": "napok",
|
||||
"DeleteSelectedApplicationsMessageText": "Biztosan törölni szeretne {count} kiválasztott importlistát?",
|
||||
"CountApplicationsSelected": "{count} Gyűjtemény(ek) kiválasztva",
|
||||
"ManageClients": "Ügyfelek kezelése"
|
||||
"ManageClients": "Ügyfelek kezelése",
|
||||
"IndexerDownloadClientHealthCheckMessage": "Indexelők érvénytelen letöltési kliensekkel: {0}.",
|
||||
"PasswordConfirmation": "Jelszó megerősítése",
|
||||
"SecretToken": "Titkos token"
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
"BackupRetentionHelpText": "Automatische veiligheidskopieën ouder dan de retentie periode zullen worden opgeruimd",
|
||||
"Backups": "Veiligheidskopieën",
|
||||
"BeforeUpdate": "Voor Update",
|
||||
"BindAddress": "Aanhaak Adres",
|
||||
"BindAddress": "Gebonden Adres",
|
||||
"BindAddressHelpText": "Geldig IP-adres, localhost of '*' voor alle interfaces",
|
||||
"Branch": "Branch",
|
||||
"BranchUpdate": "Te gebruiken branch om {appName} bij te werken",
|
||||
|
||||
@@ -195,7 +195,7 @@
|
||||
"IndexerDetails": "Detalhes do Indexador",
|
||||
"IndexerDisabled": "Indexador Desabilitado",
|
||||
"IndexerFailureRate": "Taxa de falha do indexador",
|
||||
"IndexerFlags": "Sinalizadores do indexador",
|
||||
"IndexerFlags": "Sinalizadores do Indexador",
|
||||
"IndexerHealthCheckNoIndexers": "Não há indexadores habilitados, o {appName} não retornará resultados para a pesquisa",
|
||||
"IndexerInfo": "Info do Indexador",
|
||||
"IndexerLongTermStatusCheckAllClientMessage": "Todos os indexadores estão indisponíveis devido a falhas por mais de 6 horas",
|
||||
@@ -611,5 +611,133 @@
|
||||
"NotificationsEmailSettingsUseEncryptionHelpText": "Se preferir usar criptografia se configurado no servidor, usar sempre criptografia via SSL (somente porta 465) ou StartTLS (qualquer outra porta) ou nunca usar criptografia",
|
||||
"NotificationsEmailSettingsUseEncryption": "Usar Criptografia",
|
||||
"IndexerHDBitsSettingsPasskeyHelpText": "Chave de Acesso dos Detalhes do Usuário",
|
||||
"IndexerSettingsPasskey": "Chave de Acesso"
|
||||
"IndexerSettingsPasskey": "Chave de Acesso",
|
||||
"IndexerAlphaRatioSettingsExcludeSceneHelpText": "Excluir lançamentos SCENE dos resultados",
|
||||
"IndexerAlphaRatioSettingsFreeleechOnlyHelpText": "Pesquisar lançamentos freeleech somente",
|
||||
"IndexerBeyondHDSettingsFreeleechOnlyHelpText": "Pesquisar lançamentos freeleech somente",
|
||||
"IndexerBeyondHDSettingsLimitedOnly": "Apenas Limitado",
|
||||
"IndexerBeyondHDSettingsLimitedOnlyHelpText": "Pesquisar apenas freeleech (UL limitado)",
|
||||
"IndexerBeyondHDSettingsRefundOnly": "Só Reembolso",
|
||||
"IndexerBeyondHDSettingsRefundOnlyHelpText": "Pesquisar só reembolso",
|
||||
"IndexerBeyondHDSettingsRewindOnly": "Só Retroceder",
|
||||
"IndexerBeyondHDSettingsRewindOnlyHelpText": "Pesquisar só retroceder",
|
||||
"IndexerBeyondHDSettingsRssKeyHelpText": "Chave RSS do Site (Encontrada em Minha Segurança = Chave RSS)",
|
||||
"IndexerBeyondHDSettingsSearchTypes": "Tipos de Pesquisa",
|
||||
"IndexerFileListSettingsFreeleechOnlyHelpText": "Pesquisar apenas lançamentos freeleech",
|
||||
"IndexerFileListSettingsPasskeyHelpText": "Chave de acesso do site (esta é a string alfanumérica no URL do rastreador mostrado no seu cliente de download)",
|
||||
"IndexerFileListSettingsUsernameHelpText": "Nome de Usuário do Site",
|
||||
"IndexerGazelleGamesSettingsApiKeyHelpText": "Chave API do site (encontrada em Configurações = Configurações de acesso)",
|
||||
"IndexerGazelleGamesSettingsApiKeyHelpTextWarning": "Deve ter permissões de usuário e torrents",
|
||||
"IndexerGazelleGamesSettingsSearchGroupNames": "Pesquisar Nomes de Grupos",
|
||||
"IndexerGazelleGamesSettingsSearchGroupNamesHelpText": "Pesquisar lançamentos por nomes de grupos",
|
||||
"IndexerHDBitsSettingsCodecs": "Codecs",
|
||||
"IndexerHDBitsSettingsMediumsHelpText": "se não for especificado, todas as opções serão usadas.",
|
||||
"IndexerHDBitsSettingsOriginsHelpText": "se não for especificado, todas as opções serão usadas.",
|
||||
"IndexerHDBitsSettingsUseFilenames": "Usar nomes de arquivos",
|
||||
"IndexerHDBitsSettingsUsernameHelpText": "Nome de Usuário do Site",
|
||||
"IndexerHDBitsSettingsMediums": "Meios",
|
||||
"IndexerHDBitsSettingsOrigins": "Origens",
|
||||
"IndexerIPTorrentsSettingsFreeleechOnlyHelpText": "Pesquisar apenas lançamentos freeleech",
|
||||
"IndexerNebulanceSettingsApiKeyHelpText": "Chave de API nas chaves de API das configurações do usuário. A chave deve ter permissões de Lista e Download",
|
||||
"IndexerNewznabSettingsApiKeyHelpText": "Chave de API do site",
|
||||
"IndexerNzbIndexSettingsApiKeyHelpText": "Chave de API do site",
|
||||
"IndexerOrpheusSettingsApiKeyHelpText": "Chave API do site (encontrada em Configurações = Configurações de acesso)",
|
||||
"IndexerPassThePopcornSettingsApiKeyHelpText": "Chave de API do site",
|
||||
"IndexerPassThePopcornSettingsApiUserHelpText": "Essas configurações são encontradas nas configurações de segurança do PassThePopcorn (Editar Perfil > Segurança).",
|
||||
"IndexerPassThePopcornSettingsFreeleechOnlyHelpText": "Pesquisar apenas lançamentos freeleech",
|
||||
"IndexerRedactedSettingsApiKeyHelpText": "Chave API do site (encontrada em Configurações = Configurações de acesso)",
|
||||
"IndexerSettingsAdditionalParameters": "Parâmetros Adicionais",
|
||||
"IndexerSettingsApiPath": "Caminho da API",
|
||||
"IndexerSettingsApiPathHelpText": "Caminho para a API, geralmente {url}",
|
||||
"IndexerSettingsApiUser": "Usuário da API",
|
||||
"IndexerSettingsBaseUrl": "URL Base",
|
||||
"IndexerSettingsCookie": "Cookie",
|
||||
"IndexerSettingsCookieHelpText": "Cookie do Site",
|
||||
"IndexerSettingsFreeleechOnly": "Só Freeleech",
|
||||
"IndexerSettingsGrabLimit": "Limite de Captura",
|
||||
"IndexerSettingsGrabLimitHelpText": "O número máximo de capturas conforme especificado pela respectiva unidade que {appName} permitirá ao site",
|
||||
"IndexerSettingsLimitsUnit": "Unidade de Limites",
|
||||
"IndexerSettingsLimitsUnitHelpText": "A unidade de tempo para contagem de limites por indexador",
|
||||
"IndexerSettingsPackSeedTime": "Tempo de Semeação de Pack",
|
||||
"IndexerSettingsQueryLimit": "Limite de Consultas",
|
||||
"IndexerSettingsRssKey": "Chave RSS",
|
||||
"IndexerSettingsSeedRatio": "Proporção de Semeação",
|
||||
"IndexerSettingsSeedTime": "Tempo de Semeação",
|
||||
"IndexerSettingsSeedTimeHelpText": "O tempo que um torrent deve ser semeado antes de parar, vazio usa o padrão do cliente de download",
|
||||
"IndexerSettingsVipExpiration": "Expiração VIP",
|
||||
"IndexerTorrentSyndikatSettingsApiKeyHelpText": "Chave de API do site",
|
||||
"IndexerAlphaRatioSettingsExcludeScene": "Excluir SCENE",
|
||||
"IndexerBeyondHDSettingsApiKeyHelpText": "Chave de API do site (encontrada em Minha segurança = chave de API)",
|
||||
"IndexerBeyondHDSettingsSearchTypesHelpText": "Selecione os tipos de lançamentos nos quais você está interessado. Se nenhum for selecionado, todas as opções serão usadas.",
|
||||
"IndexerHDBitsSettingsCodecsHelpText": "se não for especificado, todas as opções serão usadas.",
|
||||
"IndexerHDBitsSettingsFreeleechOnlyHelpText": "Mostrar apenas lançamentos freeleech",
|
||||
"IndexerHDBitsSettingsUseFilenamesHelpText": "Marque esta opção se quiser usar nomes de arquivos torrent como títulos de lançamento",
|
||||
"IndexerIPTorrentsSettingsCookieUserAgent": "Agente de Usuário para Cookies",
|
||||
"IndexerIPTorrentsSettingsCookieUserAgentHelpText": "Agente de Usuário associado ao cookie usado no Navegador",
|
||||
"IndexerNewznabSettingsAdditionalParametersHelpText": "Parâmetros adicionais do Newznab",
|
||||
"IndexerNewznabSettingsVipExpirationHelpText": "Insira a data (aaaa-mm-dd) para expiração do VIP ou em branco, {appName} notificará 1 semana após a expiração do VIP",
|
||||
"IndexerSettingsAppsMinimumSeeders": "Semeadores mínimos de aplicativos",
|
||||
"IndexerSettingsAppsMinimumSeedersHelpText": "Semeadores mínimos exigidos pelos aplicativos para o indexador capturar, vazio é o padrão do perfil de sincronização",
|
||||
"IndexerSettingsBaseUrlHelpText": "Selecione qual URL base {appName} usará para solicitações ao site",
|
||||
"IndexerSettingsPackSeedTimeIndexerHelpText": "O tempo que um pack de torrent (temporada ou discografia) deve ser semeado antes de parar, vazio é o padrão do aplicativo",
|
||||
"IndexerSettingsQueryLimitHelpText": "O número máximo de consultas especificadas pela respectiva unidade que {appName} permitirá ao site",
|
||||
"IndexerSettingsSeedRatioHelpText": "A proporção que um torrent deve atingir antes de parar, vazio usa o padrão do cliente de download. A proporção deve ser de pelo menos 1,0 e seguir as regras dos indexadores",
|
||||
"DefaultCategory": "Categoria Padrão",
|
||||
"Destination": "Destinação",
|
||||
"Directory": "Diretório",
|
||||
"DownloadClientDelugeSettingsUrlBaseHelpText": "Adiciona um prefixo ao URL json do deluge, consulte {url}",
|
||||
"DownloadClientFloodSettingsAdditionalTags": "Etiquetas Adicionais",
|
||||
"DownloadClientFloodSettingsAdditionalTagsHelpText": "Adiciona propriedades de mídia como etiquetas. As dicas são exemplos.",
|
||||
"DownloadClientFloodSettingsUrlBaseHelpText": "Adiciona um prefixo à API Flood, como {url}",
|
||||
"DownloadClientFreeboxSettingsApiUrl": "URL da API",
|
||||
"DownloadClientFreeboxSettingsApiUrlHelpText": "Defina o URL base da API Freebox com a versão da API, por exemplo, '{url}', o padrão é '{defaultApiUrl}'",
|
||||
"DownloadClientFreeboxSettingsAppId": "ID do App",
|
||||
"DownloadClientFreeboxSettingsAppIdHelpText": "ID do aplicativo fornecido ao criar acesso à API Freebox (ou seja, 'app_id')",
|
||||
"DownloadClientFreeboxSettingsAppToken": "Token do App",
|
||||
"DownloadClientFreeboxSettingsPortHelpText": "Porta usada para acessar a interface do Freebox, o padrão é '{port}'",
|
||||
"DownloadClientNzbgetSettingsAddPausedHelpText": "Esta opção requer pelo menos NzbGet versão 16.0",
|
||||
"DownloadClientPneumaticSettingsStrmFolder": "Pasta Strm",
|
||||
"DownloadClientPneumaticSettingsStrmFolderHelpText": "Os arquivos .strm nesta pasta serão importados pelo drone",
|
||||
"DownloadClientQbittorrentSettingsFirstAndLastFirst": "Primeiro e Último Primeiro",
|
||||
"DownloadClientQbittorrentSettingsSequentialOrder": "Ordem Sequencial",
|
||||
"DownloadClientQbittorrentSettingsSequentialOrderHelpText": "Baixe em ordem sequencial (qBittorrent 4.1.0+)",
|
||||
"DownloadClientQbittorrentSettingsUseSslHelpText": "Use uma conexão segura. Consulte Opções - UI da Web - 'Usar HTTPS em vez de HTTP' em qBittorrent.",
|
||||
"DownloadClientRTorrentSettingsAddStopped": "Adicionar Parado",
|
||||
"DownloadClientRTorrentSettingsAddStoppedHelpText": "Habilitando, irá adicionar torrents e magnets ao rTorrent em um estado parado. Isso pode quebrar os arquivos magnéticos.",
|
||||
"DownloadClientRTorrentSettingsDirectoryHelpText": "Local opcional para colocar downloads, deixe em branco para usar o local padrão do rTorrent",
|
||||
"DownloadClientRTorrentSettingsUrlPath": "Caminho da URL",
|
||||
"DownloadClientSettingsAddPaused": "Adicionar Pausado",
|
||||
"DownloadClientSettingsInitialState": "Estado Inicial",
|
||||
"DownloadClientSettingsInitialStateHelpText": "Estado inicial dos torrents adicionados a {clientName}",
|
||||
"DownloadClientSettingsPriorityItemHelpText": "Prioridade de uso ao pegar itens",
|
||||
"DownloadClientSettingsUrlBaseHelpText": "Adiciona um prefixo ao URL {clientName}, como {url}",
|
||||
"DownloadClientSettingsUseSslHelpText": "Use conexão segura ao conectar-se a {clientName}",
|
||||
"DownloadClientTransmissionSettingsUrlBaseHelpText": "Adiciona um prefixo ao URL rpc {clientName}, por exemplo, {url}, o padrão é '{defaultUrl}'",
|
||||
"TorrentBlackholeSaveMagnetFilesExtension": "Salvar Arquivos Magnet com Extensão",
|
||||
"TorrentBlackholeTorrentFolder": "Pasta do Torrent",
|
||||
"UsenetBlackholeNzbFolder": "Pasta Nzb",
|
||||
"XmlRpcPath": "Caminho RPC XML",
|
||||
"BlackholeFolderHelpText": "Pasta na qual {appName} armazenará o arquivo {extension}",
|
||||
"DownloadClientDownloadStationSettingsDirectoryHelpText": "Pasta compartilhada opcional para colocar downloads, deixe em branco para usar o local padrão do Download Station",
|
||||
"DownloadClientFloodSettingsTagsHelpText": "Etiquetas iniciais de um download. Para ser reconhecido, um download deve ter todas as etiquetas iniciais. Isso evita conflitos com downloads não relacionados.",
|
||||
"DownloadClientFreeboxSettingsAppTokenHelpText": "Token do aplicativo recuperado ao criar acesso à API Freebox (ou seja, 'app_token')",
|
||||
"DownloadClientFreeboxSettingsHostHelpText": "Nome do host ou endereço IP do host do Freebox, o padrão é '{url}' (só funcionará se estiver na mesma rede)",
|
||||
"DownloadClientPneumaticSettingsNzbFolder": "Pasta Nzb",
|
||||
"DownloadClientPneumaticSettingsNzbFolderHelpText": "Esta pasta precisará estar acessível no XBMC",
|
||||
"DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText": "Baixe a primeira e a última peças primeiro (qBittorrent 4.1.0+)",
|
||||
"DownloadClientQbittorrentSettingsInitialStateHelpText": "Estado inicial para torrents adicionados ao qBittorrent. Observe que os Torrents Forçados não obedecem às restrições de semeação",
|
||||
"DownloadClientRTorrentSettingsUrlPathHelpText": "Caminho para o endpoint XMLRPC, consulte {url}. Geralmente é RPC2 ou [caminho para ruTorrent]{url2} ao usar o ruTorrent.",
|
||||
"DownloadClientSettingsDefaultCategorySubFolderHelpText": "Categoria de reserva padrão se não existir nenhuma categoria mapeada para um lançamento. Adicionar uma categoria específica para {appName} evita conflitos com downloads não relacionados que não sejam de {appName}. Usar uma categoria é opcional, mas altamente recomendado. Cria um subdiretório [categoria] no diretório de saída.",
|
||||
"DownloadClientSettingsDefaultCategoryHelpText": "Categoria de reserva padrão se não existir nenhuma categoria mapeada para um lançamento. Adicionar uma categoria específica para {appName} evita conflitos com downloads não relacionados que não sejam de {appName}. Usar uma categoria é opcional, mas altamente recomendado.",
|
||||
"DownloadClientSettingsDestinationHelpText": "Especifica manualmente o destino do download, deixe em branco para usar o padrão",
|
||||
"DownloadClientTransmissionSettingsDirectoryHelpText": "Local opcional para colocar downloads, deixe em branco para usar o local de transmissão padrão",
|
||||
"SecretToken": "Token Secreto",
|
||||
"TorrentBlackholeSaveMagnetFiles": "Salvar Arquivos Magnets",
|
||||
"TorrentBlackholeSaveMagnetFilesExtensionHelpText": "Extensão a ser usada para links magnet, o padrão é '.magnet'",
|
||||
"TorrentBlackholeSaveMagnetFilesHelpText": "Salve o link magnet se nenhum arquivo .torrent estiver disponível (útil apenas se o cliente de download suportar magnets salvos em um arquivo)",
|
||||
"UseSsl": "Usar SSL",
|
||||
"IndexerSettingsRejectBlocklistedTorrentHashes": "Rejeitar Hashes de Torrent Bloqueados Durante a Captura",
|
||||
"IndexerSettingsRejectBlocklistedTorrentHashesHelpText": "Se um torrent for bloqueado por hash, ele pode não ser rejeitado corretamente durante o RSS/Pesquisa de alguns indexadores. Ativar isso permitirá que ele seja rejeitado após o torrent ser capturado, mas antes de ser enviado ao cliente.",
|
||||
"CustomFilter": "Filtro Personalizado",
|
||||
"LabelIsRequired": "Rótulo é requerido"
|
||||
}
|
||||
|
||||
@@ -462,5 +462,7 @@
|
||||
"EditIndexerImplementation": "Adăugați Indexator - {implementationName}",
|
||||
"EditIndexerProxyImplementation": "Adăugați proxy indexator - {implementationName}",
|
||||
"EditApplicationImplementation": "Adăugați aplicație - {implementareName}",
|
||||
"RestartProwlarr": "Reporniți {appName}"
|
||||
"RestartProwlarr": "Reporniți {appName}",
|
||||
"AddDownloadClientToProwlarr": "Adăugarea unui client de descărcare permite {appName} să trimită versiuni direct din interfața utilizatorului în timp ce se efectuează o căutare manuală",
|
||||
"ActiveApps": "Aplicații active"
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
"ApplicationStatusCheckAllClientMessage": "Hatalar nedeniyle tüm listeler kullanılamıyor",
|
||||
"CancelPendingTask": "Bu bekleyen görevi iptal etmek istediğinizden emin misiniz?",
|
||||
"DeleteTag": "Etiketi Sil",
|
||||
"BindAddressHelpText": "Tüm arayüzler için geçerli IP4 adresi veya '*'",
|
||||
"BindAddressHelpText": "Tüm arayüzler için geçerli IP adresi, localhost veya '*'",
|
||||
"ConnectSettings": "Bağlantı Ayarları",
|
||||
"DatabaseMigration": "DB Geçişi",
|
||||
"DeleteApplicationMessageText": "'{0}' bildirimini silmek istediğinizden emin misiniz?",
|
||||
@@ -136,7 +136,7 @@
|
||||
"AllIndexersHiddenDueToFilter": "Uygulanan filtre nedeniyle tüm filmler gizlendi.",
|
||||
"AnalyticsEnabledHelpText": "Anonim kullanım ve hata bilgilerini {appName} sunucularına gönderin. Bu, tarayıcınızla ilgili bilgileri, kullandığınız {appName} WebUI sayfalarını, hata raporlamasının yanı sıra işletim sistemi ve çalışma zamanı sürümünü içerir. Bu bilgileri, özellikleri ve hata düzeltmelerini önceliklendirmek için kullanacağız.",
|
||||
"ApiKey": "API Anahtarı",
|
||||
"AppDataDirectory": "AppData dizini",
|
||||
"AppDataDirectory": "Uygulama Veri Dizini",
|
||||
"NoUpdatesAreAvailable": "Güncelleme yok",
|
||||
"OAuthPopupMessage": "Pop-up'lar tarayıcınız tarafından engelleniyor",
|
||||
"Ok": "Tamam",
|
||||
@@ -306,7 +306,7 @@
|
||||
"Automatic": "Otomatik",
|
||||
"AutomaticSearch": "Otomatik Arama",
|
||||
"Backup": "Destek olmak",
|
||||
"Cancel": "İptal etmek",
|
||||
"Cancel": "Vazgeç",
|
||||
"Level": "Seviye",
|
||||
"Time": "Zaman",
|
||||
"MaintenanceRelease": "Bakım Sürümü: hata düzeltmeleri ve diğer iyileştirmeler. Daha fazla ayrıntı için Github İşlem Geçmişine bakın",
|
||||
@@ -336,8 +336,8 @@
|
||||
"ApplyTagsHelpTextAdd": "Ekle: Etiketleri mevcut etiket listesine ekleyin",
|
||||
"ApplyTagsHelpTextHowToApplyApplications": "Seçilen filmlere etiketler nasıl uygulanır",
|
||||
"ApplyTagsHelpTextRemove": "Kaldır: Girilen etiketleri kaldırın",
|
||||
"ApplyTagsHelpTextHowToApplyIndexers": "Seçilen filmlere etiketler nasıl uygulanır",
|
||||
"ApplyTagsHelpTextReplace": "Değiştir: Etiketleri girilen etiketlerle değiştirin (tüm etiketleri temizlemek için hiçbir etiket girmeyin)",
|
||||
"ApplyTagsHelpTextHowToApplyIndexers": "Seçilen indeksleyicilere etiketler nasıl uygulanır?",
|
||||
"ApplyTagsHelpTextReplace": "Değiştir: Etiketleri girilen etiketlerle değiştirin (tüm etiketleri kaldırmak için etiket girmeyin)",
|
||||
"DeleteSelectedDownloadClients": "İndirme İstemcisini Sil",
|
||||
"DownloadClientPriorityHelpText": "Birden çok İndirme İstemcisine öncelik verin. Round-Robin, aynı önceliğe sahip müşteriler için kullanılır.",
|
||||
"Genre": "Türler",
|
||||
@@ -348,7 +348,7 @@
|
||||
"RecentChanges": "Son değişiklikler",
|
||||
"minutes": "Dakika",
|
||||
"WhatsNew": "Ne var ne yok?",
|
||||
"ConnectionLostReconnect": "Radarr otomatik olarak bağlanmayı deneyecek veya aşağıdan yeniden yükle'yi tıklayabilirsiniz.",
|
||||
"ConnectionLostReconnect": "{appName} otomatik bağlanmayı deneyecek veya aşağıda yeniden yükle seçeneğini işaretleyebilirsiniz.",
|
||||
"NotificationStatusAllClientHealthCheckMessage": "Hatalar nedeniyle tüm listeler kullanılamıyor",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Hatalar nedeniyle kullanılamayan listeler: {0}",
|
||||
"Applications": "Uygulamalar",
|
||||
@@ -372,5 +372,25 @@
|
||||
"EditIndexerProxyImplementation": "Koşul Ekle - {implementationName}",
|
||||
"AddCustomFilter": "Özel Filtre Ekleyin",
|
||||
"AddDownloadClientImplementation": "İndirme İstemcisi Ekle - {implementationName}",
|
||||
"EditDownloadClientImplementation": "İndirme İstemcisi Ekle - {implementationName}"
|
||||
"EditDownloadClientImplementation": "İndirme İstemcisi Ekle - {implementationName}",
|
||||
"ApplicationURL": "Uygulama URL'si",
|
||||
"AuthenticationRequired": "Kimlik Doğrulama Gerekli",
|
||||
"ApplyChanges": "Değişiklikleri Uygula",
|
||||
"CountDownloadClientsSelected": "{count} indirme istemcisi seçildi",
|
||||
"CountIndexersSelected": "{count} dizin oluşturucu seçildi",
|
||||
"AuthenticationRequiredWarning": "Kimlik doğrulaması olmadan uzaktan erişimi engellemek için, {appName}'da artık kimlik doğrulamanın etkinleştirilmesini gerektiriyor. İsteğe bağlı olarak yerel adresler için kimlik doğrulamayı devre dışı bırakabilirsiniz.",
|
||||
"Clone": "Klon",
|
||||
"Category": "Kategori",
|
||||
"AppUpdated": "{appName} Güncellendi",
|
||||
"AppUpdatedVersion": "{appName}, `{version}` sürümüne güncellendi; en son değişikliklerin etkin olabilmesi için {appName} uygulamasını yeniden başlatmanız gerekli",
|
||||
"ApplicationUrlHelpText": "Bu uygulamanın http(s)://, bağlantı noktası ve URL tabanını içeren harici URL'si",
|
||||
"AuthenticationRequiredPasswordConfirmationHelpTextWarning": "Yeni şifreyi onayla",
|
||||
"AuthenticationRequiredPasswordHelpTextWarning": "Yeni şifre girin",
|
||||
"AuthenticationRequiredUsernameHelpTextWarning": "Yeni kullanıcı adınızı girin",
|
||||
"ConnectionLostToBackend": "{appName}'ın arka uçla bağlantısı kesildi ve işlevselliğin geri kazanılması için yeniden yüklenmesi gerekecek.",
|
||||
"BlackholeFolderHelpText": "{appName} uygulamasının {extension} dosyasını depolayacağı klasör",
|
||||
"AuthenticationMethod": "Kimlik Doğrulama Yöntemi",
|
||||
"AuthenticationMethodHelpTextWarning": "Lütfen geçerli bir kimlik doğrulama yöntemi seçin",
|
||||
"AuthenticationRequiredHelpText": "İstekler için Kimlik doğrulamanın gereklilik ayarını değiştirin. Riskleri anlamadığınız sürece değiştirmeyin.",
|
||||
"CustomFilter": "Özel Filtre"
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Prowlarr.Api.V1.Indexers
|
||||
public double? SeedRatio { get; set; }
|
||||
public int? SeedTime { get; set; }
|
||||
public int? PackSeedTime { get; set; }
|
||||
public bool? RejectBlocklistedTorrentHashesWhileGrabbing { get; set; }
|
||||
}
|
||||
|
||||
public class IndexerBulkResourceMapper : ProviderBulkResourceMapper<IndexerBulkResource, IndexerDefinition>
|
||||
@@ -35,6 +36,7 @@ namespace Prowlarr.Api.V1.Indexers
|
||||
((ITorrentIndexerSettings)existing.Settings).TorrentBaseSettings.SeedRatio = resource.SeedRatio ?? ((ITorrentIndexerSettings)existing.Settings).TorrentBaseSettings.SeedRatio;
|
||||
((ITorrentIndexerSettings)existing.Settings).TorrentBaseSettings.SeedTime = resource.SeedTime ?? ((ITorrentIndexerSettings)existing.Settings).TorrentBaseSettings.SeedTime;
|
||||
((ITorrentIndexerSettings)existing.Settings).TorrentBaseSettings.PackSeedTime = resource.PackSeedTime ?? ((ITorrentIndexerSettings)existing.Settings).TorrentBaseSettings.PackSeedTime;
|
||||
((ITorrentIndexerSettings)existing.Settings).TorrentBaseSettings.RejectBlocklistedTorrentHashesWhileGrabbing = resource.RejectBlocklistedTorrentHashesWhileGrabbing ?? ((ITorrentIndexerSettings)existing.Settings).TorrentBaseSettings.RejectBlocklistedTorrentHashesWhileGrabbing;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -5016,6 +5016,10 @@
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"nullable": true
|
||||
},
|
||||
"rejectBlocklistedTorrentHashesWhileGrabbing": {
|
||||
"type": "boolean",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -2328,9 +2328,9 @@ camelcase@^5.3.1:
|
||||
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
|
||||
|
||||
caniuse-lite@^1.0.30001464, caniuse-lite@^1.0.30001517:
|
||||
version "1.0.30001525"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001525.tgz#d2e8fdec6116ffa36284ca2c33ef6d53612fe1c8"
|
||||
integrity sha512-/3z+wB4icFt3r0USMwxujAqRvaD/B7rvGTsKhbhSQErVrJvkZCLhgNLJxU8MevahQVH6hCU9FsHdNUFbiwmE7Q==
|
||||
version "1.0.30001591"
|
||||
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz"
|
||||
integrity sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==
|
||||
|
||||
chalk@^1.1.3:
|
||||
version "1.1.3"
|
||||
|
||||
Reference in New Issue
Block a user