mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2026-04-17 21:44:48 -04:00
Compare commits
25 Commits
rate-limit
...
v1.1.0.232
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1529527af9 | ||
|
|
a11bd1c3c7 | ||
|
|
915b320a4a | ||
|
|
155f72cc45 | ||
|
|
3f73fec5c3 | ||
|
|
8515623ceb | ||
|
|
963cddb582 | ||
|
|
ede323b8ed | ||
|
|
07d7fc98b0 | ||
|
|
1b78fd38db | ||
|
|
5a9d4d6280 | ||
|
|
70685de5d2 | ||
|
|
9860183433 | ||
|
|
50331c61ae | ||
|
|
bd3408f170 | ||
|
|
c043bf8da9 | ||
|
|
ea3fa6f28d | ||
|
|
8917347c0b | ||
|
|
2cebdf4a06 | ||
|
|
985110cfb9 | ||
|
|
de876247a3 | ||
|
|
bad6c301f8 | ||
|
|
fc3b23394a | ||
|
|
92c3656bad | ||
|
|
1acbee2a57 |
@@ -142,8 +142,8 @@ module.exports = (env) => {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js?$/,
|
||||
exclude: /(node_modules|JsLibraries)/,
|
||||
test: /\.jsx?$/,
|
||||
exclude: /[\\/]node_modules[\\/](?!(@sentry\/browser|@sentry\/integrations|chart.js|filesize|normalize.css)[\\/])/,
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader',
|
||||
|
||||
@@ -79,6 +79,7 @@ class IndexerIndexRow extends Component {
|
||||
privacy,
|
||||
priority,
|
||||
status,
|
||||
fields,
|
||||
appProfile,
|
||||
added,
|
||||
capabilities,
|
||||
@@ -96,6 +97,8 @@ class IndexerIndexRow extends Component {
|
||||
isIndexerInfoModalOpen
|
||||
} = this.state;
|
||||
|
||||
const baseUrl = fields.find((field) => field.name === 'baseUrl')?.value ?? indexerUrls[0];
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
@@ -250,7 +253,7 @@ class IndexerIndexRow extends Component {
|
||||
className={styles.externalLink}
|
||||
name={icons.EXTERNAL_LINK}
|
||||
title={translate('Website')}
|
||||
to={indexerUrls[0].replace('api.', '')}
|
||||
to={baseUrl.replace('api.', '')}
|
||||
/> : null
|
||||
}
|
||||
|
||||
@@ -299,6 +302,7 @@ IndexerIndexRow.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
enable: PropTypes.bool.isRequired,
|
||||
redirect: PropTypes.bool.isRequired,
|
||||
fields: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
appProfile: PropTypes.object.isRequired,
|
||||
status: PropTypes.object,
|
||||
capabilities: PropTypes.object,
|
||||
|
||||
@@ -20,11 +20,14 @@ function IndexerInfoModalContent(props) {
|
||||
encoding,
|
||||
language,
|
||||
indexerUrls,
|
||||
fields,
|
||||
protocol,
|
||||
capabilities,
|
||||
onModalClose
|
||||
} = props;
|
||||
|
||||
const baseUrl = fields.find((field) => field.name === 'baseUrl')?.value ?? indexerUrls[0];
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
@@ -57,7 +60,7 @@ function IndexerInfoModalContent(props) {
|
||||
/>
|
||||
<DescriptionListItemTitle>{translate('IndexerSite')}</DescriptionListItemTitle>
|
||||
<DescriptionListItemDescription>
|
||||
<Link to={indexerUrls[0]}>{indexerUrls[0]}</Link>
|
||||
<Link to={baseUrl}>{baseUrl}</Link>
|
||||
</DescriptionListItemDescription>
|
||||
<DescriptionListItemTitle>{`${protocol === 'usenet' ? 'Newznab' : 'Torznab'} Url`}</DescriptionListItemTitle>
|
||||
<DescriptionListItemDescription>
|
||||
@@ -114,6 +117,7 @@ IndexerInfoModalContent.propTypes = {
|
||||
encoding: PropTypes.string.isRequired,
|
||||
language: PropTypes.string.isRequired,
|
||||
indexerUrls: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
fields: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
protocol: PropTypes.string.isRequired,
|
||||
capabilities: PropTypes.object.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
"lint": "esprint check",
|
||||
"lint-fix": "esprint check --fix",
|
||||
"stylelint-linux": "stylelint $(find frontend -name '*.css') --config frontend/.stylelintrc",
|
||||
"stylelint-windows": "stylelint frontend/**/*.css --config frontend/.stylelintrc"
|
||||
"stylelint-windows": "stylelint frontend/**/*.css --config frontend/.stylelintrc",
|
||||
"check-modules": "are-you-es5 check . -r"
|
||||
},
|
||||
"repository": "https://github.com/Prowlarr/Prowlarr",
|
||||
"author": "Team Prowlarr",
|
||||
@@ -93,6 +94,7 @@
|
||||
"@babel/plugin-syntax-dynamic-import": "7.8.3",
|
||||
"@babel/preset-env": "7.20.2",
|
||||
"@babel/preset-react": "7.18.6",
|
||||
"are-you-es5": "2.1.2",
|
||||
"autoprefixer": "10.4.13",
|
||||
"babel-loader": "9.1.0",
|
||||
"babel-plugin-inline-classnames": "2.0.1",
|
||||
|
||||
@@ -53,6 +53,26 @@ namespace NzbDrone.Common.Test.Http
|
||||
newUri.FullUri.Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestCase("", "./relative", "relative")]
|
||||
[TestCase("/", "./relative", "/relative")]
|
||||
[TestCase("/base", "./relative", "/relative")]
|
||||
[TestCase("/base/sub", "./relative", "/base/relative")]
|
||||
[TestCase("/base/sub/", "./relative", "/base/sub/relative")]
|
||||
[TestCase("base/sub", "./relative", "base/relative")]
|
||||
[TestCase("base/sub/", "./relative", "base/sub/relative")]
|
||||
[TestCase("", "../relative", "relative")]
|
||||
[TestCase("/", "../relative", "/relative")]
|
||||
[TestCase("/base", "../relative", "/relative")]
|
||||
[TestCase("/base/sub", "../relative", "/base/relative")]
|
||||
[TestCase("/base/sub/", "../relative", "/base/sub/relative")]
|
||||
[TestCase("base/sub", "../relative", "base/relative")]
|
||||
[TestCase("base/sub/", "../relative", "base/sub/relative")]
|
||||
public void should_combine_uri_with_dot_segment(string basePath, string relativePath, string expected)
|
||||
{
|
||||
var newUri = new HttpUri(basePath) + new HttpUri(relativePath);
|
||||
newUri.FullUri.Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestCase("", "", "")]
|
||||
[TestCase("/", "", "/")]
|
||||
[TestCase("base", "", "base")]
|
||||
|
||||
@@ -24,12 +24,16 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
||||
[TestCase(@"https://beyond-hd.me/api/torrents/2b51db35e1912ffc138825a12b9933d2")]
|
||||
[TestCase(@"Req: [POST] https://www3.yggtorrent.nz/user/login: id=mySecret&pass=mySecret&ci_csrf_token=2b51db35e1912ffc138825a12b9933d2")]
|
||||
[TestCase(@"https://torrentseeds.org/api/torrents/filter?api_token=2b51db35e1912ffc138825a12b9933d2&name=&sortField=created_at&sortDirection=desc&perPage=100&page=1")]
|
||||
[TestCase(@"https://beyond-hd.me/torrent/download/the-next-365-days-2022-2160p-nf-web-dl-dual-ddp-51-dovi-hdr-hevc-apex.225146.2b51db35e1912ffc138825a12b9933d2")]
|
||||
[TestCase(@"https://anthelion.me/api.php?api_key=2b51db35e1910123321025a12b9933d2&o=json&t=movie&q=&tmdb=&imdb=&cat=&limit=100&offset=0")]
|
||||
[TestCase(@"https://avistaz.to/api/v1/jackett/auth: username=mySecret&password=mySecret&pid=mySecret")]
|
||||
|
||||
// Indexer and Download Client Responses
|
||||
|
||||
// avistaz response
|
||||
[TestCase(@"""download"":""https://avistaz.to/rss/download/2b51db35e1910123321025a12b9933d2/tb51db35e1910123321025a12b9933d2.torrent"",")]
|
||||
[TestCase(@",""info_hash"":""2b51db35e1910123321025a12b9933d2"",")]
|
||||
[TestCase(@"""token"":""2b51db35e1910123321025a12b9933d2""")]
|
||||
|
||||
// animebytes response
|
||||
[TestCase(@"""Link"":""https://animebytes.tv/torrent/994064/download/tb51db35e1910123321025a12b9933d2"",")]
|
||||
|
||||
@@ -76,6 +76,7 @@ namespace NzbDrone.Common.Http
|
||||
get
|
||||
{
|
||||
var newUrl = Headers["Location"];
|
||||
|
||||
if (newUrl == null)
|
||||
{
|
||||
newUrl = Headers["Refresh"];
|
||||
|
||||
@@ -166,6 +166,37 @@ namespace NzbDrone.Common.Http
|
||||
return relativePath;
|
||||
}
|
||||
|
||||
if (relativePath.StartsWith("./"))
|
||||
{
|
||||
relativePath = relativePath.TrimStart('.').TrimStart('/');
|
||||
|
||||
var lastIndex = basePath.LastIndexOf("/");
|
||||
|
||||
if (lastIndex > 0)
|
||||
{
|
||||
basePath = basePath.Substring(0, lastIndex) + "/";
|
||||
}
|
||||
}
|
||||
|
||||
if (relativePath.StartsWith("../"))
|
||||
{
|
||||
relativePath = relativePath.TrimStart('.').TrimStart('/');
|
||||
|
||||
var lastIndex = basePath.LastIndexOf("/");
|
||||
|
||||
if (lastIndex > 0)
|
||||
{
|
||||
basePath = basePath.Substring(0, lastIndex) + "/";
|
||||
}
|
||||
|
||||
var secondLastIndex = basePath.LastIndexOf("/");
|
||||
|
||||
if (lastIndex > 0)
|
||||
{
|
||||
basePath = basePath.Substring(0, secondLastIndex) + "/";
|
||||
}
|
||||
}
|
||||
|
||||
var baseSlashIndex = basePath.LastIndexOf('/');
|
||||
|
||||
if (baseSlashIndex >= 0)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -8,10 +7,10 @@ namespace NzbDrone.Common.Instrumentation
|
||||
{
|
||||
public class CleanseLogMessage
|
||||
{
|
||||
private static readonly Regex[] CleansingRules = new[]
|
||||
{
|
||||
private static readonly Regex[] CleansingRules =
|
||||
{
|
||||
// Url
|
||||
new Regex(@"(?<=[?&: ;])(apikey|(?:(?:access|api)[-_]?)?token|pass(?:key|wd)?|auth|authkey|user|u?id|api|[a-z_]*apikey|account|pwd)=(?<secret>[^&=""]+?)(?=[ ""&=]|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"(?<=[?&: ;])(apikey|api_key|(?:(?:access|api)[-_]?)?token|pass(?:key|wd)?|auth|authkey|user|u?id|api|[a-z_]*apikey|account|pid|pwd)=(?<secret>[^&=""]+?)(?=[ ""&=]|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"(?<=[?& ;])[^=]*?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"rss\.torrentleech\.org/(?!rss)(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"rss\.torrentleech\.org/rss/download/[0-9]+/(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
@@ -21,6 +20,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
new Regex(@"\b(\w*)?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"(?<=authkey = "")(?<secret>[^&=]+?)(?="")", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"(?<=beyond-hd\.[a-z]+/api/torrents/)(?<secret>[^&=][a-z0-9]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"(?<=beyond-hd\.[a-z]+/torrent/download/[\w\d-]+[.]\d+[.])(?<secret>[a-z0-9]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// UNIT3D
|
||||
new Regex(@"(?<=[a-z0-9-]+\.[a-z]+/torrent/download/\d+\.)(?<secret>[^&=][a-z0-9]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
@@ -58,9 +58,10 @@ namespace NzbDrone.Common.Instrumentation
|
||||
new Regex(@"(?:avistaz|exoticaz|cinemaz|privatehd)\.[a-z]{2,3}/rss/download/(?<secret>[^&=]+?)/(?<secret>[^&=]+?)\.torrent", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"(?:animebytes)\.[a-z]{2,3}/torrent/[0-9]+/download/(?<secret>[^&=]+?)[""]", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@",""info_hash"":""(?<secret>[^&=]+?)"",", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"""token"":""(?<secret>[^&=]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@",""pass[- _]?key"":""(?<secret>[^&=]+?)"",", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@",""rss[- _]?key"":""(?<secret>[^&=]+?)"",", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
};
|
||||
};
|
||||
|
||||
private static readonly Regex CleanseRemoteIPRegex = new Regex(@"(?:Auth-\w+(?<!Failure|Unauthorized) ip|from) (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", RegexOptions.Compiled);
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AvistazTests
|
||||
torrentInfo.InfoUrl.Should().Be("https://avistaz.to/torrent/187240-japan-sinks-people-of-hope-2021-s01e05-720p-nf-web-dl-ddp20-x264-seikel");
|
||||
torrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2021-11-15 04:26:21"));
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2021-11-14 22:26:21"));
|
||||
torrentInfo.Size.Should().Be(935127615);
|
||||
torrentInfo.InfoHash.Should().Be("a879261d4e6e792402f92401141a21de70d51bf2");
|
||||
torrentInfo.MagnetUrl.Should().Be(null);
|
||||
|
||||
@@ -21,10 +21,15 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Subject.Definition = new IndexerDefinition()
|
||||
Subject.Definition = new IndexerDefinition
|
||||
{
|
||||
Name = "FileList",
|
||||
Settings = new FileListSettings() { Username = "someuser", Passkey = "somepass" }
|
||||
Settings = new FileListSettings
|
||||
{
|
||||
BaseUrl = "https://filelist.io/",
|
||||
Username = "someuser",
|
||||
Passkey = "somepass"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -35,9 +40,9 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
|
||||
|
||||
Mocker.GetMock<IIndexerHttpClient>()
|
||||
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get), Subject.Definition))
|
||||
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed)));
|
||||
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader { { "Content-Type", "application/json" } }, new CookieCollection(), recentFeed)));
|
||||
|
||||
var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } })).Releases;
|
||||
var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new[] { 2000 } })).Releases;
|
||||
|
||||
releases.Should().HaveCount(4);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -50,12 +55,14 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
|
||||
torrentInfo.InfoUrl.Should().Be("https://filelist.io/details.php?id=665873");
|
||||
torrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2020-01-25 20:20:19").ToUniversalTime());
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2020-01-25 20:20:19"));
|
||||
torrentInfo.Size.Should().Be(8300512414);
|
||||
torrentInfo.InfoHash.Should().Be(null);
|
||||
torrentInfo.MagnetUrl.Should().Be(null);
|
||||
torrentInfo.Peers.Should().Be(2 + 12);
|
||||
torrentInfo.Seeders.Should().Be(12);
|
||||
|
||||
releases.Any(t => t.IndexerFlags.Contains(IndexerFlag.Internal)).Should().Be(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,34 +16,35 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Subject.Settings = new FileListSettings()
|
||||
Subject.Settings = new FileListSettings
|
||||
{
|
||||
BaseUrl = "https://filelist.io/",
|
||||
Passkey = "abcd",
|
||||
Username = "somename",
|
||||
BaseUrl = "https://filelist.io"
|
||||
Username = "somename"
|
||||
};
|
||||
|
||||
Subject.Capabilities = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.ImdbId, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
BookSearchParam.Q
|
||||
},
|
||||
{
|
||||
BookSearchParam.Q
|
||||
},
|
||||
Flags = new List<IndexerFlag>
|
||||
{
|
||||
IndexerFlag.FreeLeech
|
||||
IndexerFlag.FreeLeech,
|
||||
IndexerFlag.Internal,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -53,7 +54,7 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
|
||||
_movieSearchCriteria = new MovieSearchCriteria
|
||||
{
|
||||
SearchTerm = "Star Wars",
|
||||
Categories = new int[] { 2000 }
|
||||
Categories = new[] { 2000 }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -65,13 +66,13 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
|
||||
[Test]
|
||||
public void should_use_categories_for_feed()
|
||||
{
|
||||
var results = Subject.GetSearchRequests(new MovieSearchCriteria { Categories = new int[] { NewznabStandardCategory.MoviesSD.Id, NewznabStandardCategory.MoviesDVD.Id } });
|
||||
var results = Subject.GetSearchRequests(new MovieSearchCriteria { Categories = new[] { NewznabStandardCategory.MoviesSD.Id, NewznabStandardCategory.MoviesDVD.Id } });
|
||||
|
||||
results.GetAllTiers().Should().HaveCount(1);
|
||||
|
||||
var page = results.GetAllTiers().First().First();
|
||||
|
||||
page.Url.Query.Should().Contain("&category=1,2&");
|
||||
page.Url.Query.Should().Contain("&category=1%2C2");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -100,7 +101,7 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
|
||||
var page = results.GetAllTiers().First().First();
|
||||
|
||||
page.Url.Query.Should().Contain("type=name");
|
||||
page.Url.Query.Should().Contain("query=Star Wars");
|
||||
page.Url.Query.Should().Contain("query=Star+Wars");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace NzbDrone.Core.Test.IndexerTests.OrpheusTests
|
||||
Subject.Definition = new IndexerDefinition()
|
||||
{
|
||||
Name = "Orpheus",
|
||||
Settings = new OrpheusSettings() { Apikey = "somekey" }
|
||||
Settings = new OrpheusSettings { Apikey = "somekey" }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -37,14 +37,14 @@ namespace NzbDrone.Core.Test.IndexerTests.OrpheusTests
|
||||
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get), Subject.Definition))
|
||||
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader { { "Content-Type", "application/json" } }, new CookieCollection(), recentFeed)));
|
||||
|
||||
var releases = (await Subject.Fetch(new BasicSearchCriteria { Categories = new int[] { 2000 } })).Releases;
|
||||
var releases = (await Subject.Fetch(new BasicSearchCriteria { Categories = new[] { 2000 } })).Releases;
|
||||
|
||||
releases.Should().HaveCount(65);
|
||||
releases.First().Should().BeOfType<GazelleInfo>();
|
||||
|
||||
var torrentInfo = releases.First() as GazelleInfo;
|
||||
|
||||
torrentInfo.Title.Should().Be("The Beatles - Abbey Road (1969) [MP3 V2 (VBR)] [BD]");
|
||||
torrentInfo.Title.Should().Be("The Beatles - Abbey Road (1969) [2.0 Mix 2019] [MP3 V2 (VBR)] [BD]");
|
||||
torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
torrentInfo.DownloadUrl.Should().Be("https://orpheus.network/ajax.php?action=download&id=1902448");
|
||||
torrentInfo.InfoUrl.Should().Be("https://orpheus.network/torrents.php?id=466&torrentid=1902448");
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(025)]
|
||||
public class speedcd_userpasssettings_to_speedcdsettings : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Update.Table("Indexers").Set(new { ConfigContract = "SpeedCDSettings" }).Where(new { Implementation = "SpeedCD" });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,11 @@ namespace NzbDrone.Core.Http.CloudFlare
|
||||
response.StatusCode.Equals(HttpStatusCode.Forbidden))
|
||||
{
|
||||
var responseHtml = response.Content;
|
||||
if (responseHtml.Contains("<title>Just a moment...") || responseHtml.Contains("<title>DDOS-GUARD"))
|
||||
if (responseHtml.Contains("<title>Just a moment...</title>") ||
|
||||
responseHtml.Contains("<title>Access denied</title>") ||
|
||||
responseHtml.Contains("<title>Attention Required! | Cloudflare</title>") ||
|
||||
responseHtml.Trim().Equals("error code: 1020") ||
|
||||
responseHtml.Contains("<title>DDOS-GUARD</title>"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace NzbDrone.Core.IndexerVersions
|
||||
/* Update Service will fall back if version # does not exist for an indexer per Ta */
|
||||
|
||||
private const string DEFINITION_BRANCH = "master";
|
||||
private const int DEFINITION_VERSION = 7;
|
||||
private const int DEFINITION_VERSION = 8;
|
||||
|
||||
//Used when moving yml to C#
|
||||
private readonly List<string> _defintionBlocklist = new List<string>()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Definitions.Avistaz;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
@@ -10,18 +9,23 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public class AvistaZ : AvistazBase
|
||||
{
|
||||
public override string Name => "AvistaZ";
|
||||
public override string[] IndexerUrls => new string[] { "https://avistaz.to/" };
|
||||
public override string[] IndexerUrls => new[] { "https://avistaz.to/" };
|
||||
public override string Description => "Aka AsiaTorrents";
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
|
||||
public AvistaZ(IIndexerRepository indexerRepository, IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
public AvistaZ(IIndexerRepository indexerRepository,
|
||||
IIndexerHttpClient httpClient,
|
||||
IEventAggregator eventAggregator,
|
||||
IIndexerStatusService indexerStatusService,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
: base(indexerRepository, httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new AvistazRequestGenerator()
|
||||
return new AvistazRequestGenerator
|
||||
{
|
||||
Settings = Settings,
|
||||
HttpClient = _httpClient,
|
||||
@@ -30,18 +34,23 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
};
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new AvistaZParser();
|
||||
}
|
||||
|
||||
protected override IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId, TvSearchParam.TvdbId, TvSearchParam.Genre
|
||||
},
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId, TvSearchParam.TvdbId, TvSearchParam.Genre
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId, MovieSearchParam.TmdbId, MovieSearchParam.Genre
|
||||
}
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId, MovieSearchParam.TmdbId, MovieSearchParam.Genre
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Movies);
|
||||
@@ -52,9 +61,13 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TVUHD);
|
||||
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TVHD);
|
||||
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TVSD);
|
||||
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.Audio);
|
||||
|
||||
return caps;
|
||||
}
|
||||
}
|
||||
|
||||
public class AvistaZParser : AvistazParserBase
|
||||
{
|
||||
protected override string TimezoneOffset => "+01:00";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
|
||||
@@ -13,12 +13,11 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
public abstract class AvistazBase : TorrentIndexerBase<AvistazSettings>
|
||||
{
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override string[] IndexerUrls => new string[] { "" };
|
||||
protected virtual string LoginUrl => Settings.BaseUrl + "api/v1/jackett/auth";
|
||||
public override bool SupportsRss => true;
|
||||
public override bool SupportsSearch => true;
|
||||
public override int PageSize => 50;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
protected virtual string LoginUrl => Settings.BaseUrl + "api/v1/jackett/auth";
|
||||
private IIndexerRepository _indexerRepository;
|
||||
|
||||
public AvistazBase(IIndexerRepository indexerRepository,
|
||||
@@ -34,7 +33,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new AvistazRequestGenerator()
|
||||
return new AvistazRequestGenerator
|
||||
{
|
||||
Settings = Settings,
|
||||
HttpClient = _httpClient,
|
||||
@@ -45,14 +44,12 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new AvistazParser();
|
||||
return new AvistazParserBase();
|
||||
}
|
||||
|
||||
protected virtual IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities();
|
||||
|
||||
return caps;
|
||||
return new IndexerCapabilities();
|
||||
}
|
||||
|
||||
protected override async Task DoLogin()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -10,13 +11,10 @@ using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
{
|
||||
public class AvistazParser : IParseIndexerResponse
|
||||
public class AvistazParserBase : IParseIndexerResponse
|
||||
{
|
||||
private readonly HashSet<string> _hdResolutions = new HashSet<string> { "1080p", "1080i", "720p" };
|
||||
|
||||
public AvistazParser()
|
||||
{
|
||||
}
|
||||
protected virtual string TimezoneOffset => "-05:00"; // Avistaz does not specify a timezone & returns server time
|
||||
private readonly HashSet<string> _hdResolutions = new () { "1080p", "1080i", "720p" };
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
|
||||
@@ -61,7 +59,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
InfoUrl = details,
|
||||
Guid = details,
|
||||
Categories = cats,
|
||||
PublishDate = DateTime.Parse(row.CreatedAt + "-05:00").ToUniversalTime(), // Avistaz does not specify a timezone & returns server time
|
||||
PublishDate = DateTime.Parse($"{row.CreatedAt} {TimezoneOffset}", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal),
|
||||
Size = row.FileSize,
|
||||
Files = row.FileCount,
|
||||
Grabs = row.Completed,
|
||||
@@ -12,17 +12,12 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
public class AvistazRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
public AvistazSettings Settings { get; set; }
|
||||
|
||||
public IDictionary<string, string> AuthCookieCache { get; set; }
|
||||
public IIndexerHttpClient HttpClient { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
public Logger Logger { get; set; }
|
||||
|
||||
protected virtual string SearchUrl => Settings.BaseUrl + "api/v1/jackett/torrents";
|
||||
protected virtual bool ImdbInTags => false;
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
protected virtual string SearchUrl => Settings.BaseUrl + "api/v1/jackett/torrents";
|
||||
|
||||
// hook to adjust the search category
|
||||
protected virtual List<KeyValuePair<string, string>> GetBasicSearchParameters(int[] categories, string genre)
|
||||
@@ -45,7 +40,8 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
}
|
||||
|
||||
// resolution filter to improve the search
|
||||
if (!categories.Contains(NewznabStandardCategory.Movies.Id) && !categories.Contains(NewznabStandardCategory.TV.Id) &&
|
||||
if (!categories.Contains(NewznabStandardCategory.Movies.Id) &&
|
||||
!categories.Contains(NewznabStandardCategory.TV.Id) &&
|
||||
!categories.Contains(NewznabStandardCategory.Audio.Id))
|
||||
{
|
||||
if (categories.Contains(NewznabStandardCategory.MoviesUHD.Id) || categories.Contains(NewznabStandardCategory.TVUHD.Id))
|
||||
|
||||
@@ -2,7 +2,7 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using AngleSharp.Dom;
|
||||
@@ -680,6 +680,12 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
case "urlencode":
|
||||
data = data.UrlEncode(_encoding);
|
||||
break;
|
||||
case "htmldecode":
|
||||
data = WebUtility.HtmlDecode(data);
|
||||
break;
|
||||
case "htmlencode":
|
||||
data = WebUtility.HtmlEncode(data);
|
||||
break;
|
||||
case "timeago":
|
||||
case "reltime":
|
||||
data = DateTimeUtil.FromTimeAgo(data).ToString(DateTimeUtil.Rfc1123ZPattern);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Definitions.Avistaz;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
@@ -10,18 +9,23 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public class CinemaZ : AvistazBase
|
||||
{
|
||||
public override string Name => "CinemaZ";
|
||||
public override string[] IndexerUrls => new string[] { "https://cinemaz.to/" };
|
||||
public override string[] IndexerUrls => new[] { "https://cinemaz.to/" };
|
||||
public override string Description => "CinemaZ (EuTorrents) is a Private Torrent Tracker for FOREIGN NON-ASIAN MOVIES.";
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
|
||||
public CinemaZ(IIndexerRepository indexerRepository, IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
public CinemaZ(IIndexerRepository indexerRepository,
|
||||
IIndexerHttpClient httpClient,
|
||||
IEventAggregator eventAggregator,
|
||||
IIndexerStatusService indexerStatusService,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
: base(indexerRepository, httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new AvistazRequestGenerator()
|
||||
return new AvistazRequestGenerator
|
||||
{
|
||||
Settings = Settings,
|
||||
HttpClient = _httpClient,
|
||||
@@ -52,7 +56,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TVUHD);
|
||||
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TVHD);
|
||||
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TVSD);
|
||||
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.Audio);
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
@@ -10,18 +10,23 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public class ExoticaZ : AvistazBase
|
||||
{
|
||||
public override string Name => "ExoticaZ";
|
||||
public override string[] IndexerUrls => new string[] { "https://exoticaz.to/" };
|
||||
public override string[] IndexerUrls => new[] { "https://exoticaz.to/" };
|
||||
public override string Description => "ExoticaZ (YourExotic) is a Private Torrent Tracker for 3X";
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
|
||||
public ExoticaZ(IIndexerRepository indexerRepository, IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
public ExoticaZ(IIndexerRepository indexerRepository,
|
||||
IIndexerHttpClient httpClient,
|
||||
IEventAggregator eventAggregator,
|
||||
IIndexerStatusService indexerStatusService,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
: base(indexerRepository, httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new AvistazRequestGenerator()
|
||||
return new AvistazRequestGenerator
|
||||
{
|
||||
Settings = Settings,
|
||||
HttpClient = _httpClient,
|
||||
@@ -52,7 +57,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
}
|
||||
}
|
||||
|
||||
public class ExoticaZParser : AvistazParser
|
||||
public class ExoticaZParser : AvistazParserBase
|
||||
{
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
||||
@@ -9,7 +8,8 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
public class FileList : TorrentIndexerBase<FileListSettings>
|
||||
{
|
||||
public override string Name => "FileList.io";
|
||||
public override string[] IndexerUrls => new string[] { "https://filelist.io" };
|
||||
public override string[] IndexerUrls => new[] { "https://filelist.io/" };
|
||||
public override string[] LegacyUrls => new[] { "https://filelist.io" };
|
||||
public override string Description => "FileList (FL) is a ROMANIAN Private Torrent Tracker for 0DAY / GENERAL";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
@@ -18,14 +18,18 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
public override bool SupportsRedirect => true;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
public FileList(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
public FileList(IIndexerHttpClient httpClient,
|
||||
IEventAggregator eventAggregator,
|
||||
IIndexerStatusService indexerStatusService,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new FileListRequestGenerator() { Settings = Settings, Capabilities = Capabilities };
|
||||
return new FileListRequestGenerator { Settings = Settings, Capabilities = Capabilities };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
@@ -38,21 +42,21 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.ImdbId, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.ImdbId, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
BookSearchParam.Q
|
||||
},
|
||||
{
|
||||
BookSearchParam.Q
|
||||
},
|
||||
Flags = new List<IndexerFlag>
|
||||
{
|
||||
IndexerFlag.Internal,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.FileList
|
||||
@@ -24,5 +23,7 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
[JsonProperty(PropertyName = "upload_date")]
|
||||
public string UploadDate { get; set; }
|
||||
public string Category { get; set; }
|
||||
[JsonProperty(PropertyName = "small_description")]
|
||||
public string SmallDescription { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Newtonsoft.Json;
|
||||
using NzbDrone.Common.Http;
|
||||
@@ -25,9 +27,12 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
|
||||
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new IndexerException(indexerResponse,
|
||||
"Unexpected response status {0} code from API request",
|
||||
indexerResponse.HttpResponse.StatusCode);
|
||||
throw new IndexerException(indexerResponse, "Unexpected response status {0} code from API request", indexerResponse.HttpResponse.StatusCode);
|
||||
}
|
||||
|
||||
if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value))
|
||||
{
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from API request, expected {HttpAccept.Json.Value}");
|
||||
}
|
||||
|
||||
var queryResults = JsonConvert.DeserializeObject<List<FileListTorrent>>(indexerResponse.Content);
|
||||
@@ -49,10 +54,10 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
imdbId = int.Parse(result.ImdbId.Substring(2));
|
||||
}
|
||||
|
||||
var downloadVolumeFactor = result.FreeLeech == true ? 0 : 1;
|
||||
var uploadVolumeFactor = result.DoubleUp == true ? 2 : 1;
|
||||
var downloadVolumeFactor = result.FreeLeech ? 0 : 1;
|
||||
var uploadVolumeFactor = result.DoubleUp ? 2 : 1;
|
||||
|
||||
torrentInfos.Add(new TorrentInfo()
|
||||
torrentInfos.Add(new TorrentInfo
|
||||
{
|
||||
Guid = string.Format("FileList-{0}", id),
|
||||
Title = result.Name,
|
||||
@@ -62,7 +67,9 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
InfoUrl = GetInfoUrl(id),
|
||||
Seeders = result.Seeders,
|
||||
Peers = result.Leechers + result.Seeders,
|
||||
PublishDate = DateTime.Parse(result.UploadDate + " +0200"),
|
||||
PublishDate = DateTime.Parse(result.UploadDate + " +0200", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal),
|
||||
Description = result.SmallDescription,
|
||||
Genres = result.SmallDescription.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToList(),
|
||||
ImdbId = imdbId,
|
||||
IndexerFlags = flags,
|
||||
Files = (int)result.Files,
|
||||
@@ -70,7 +77,7 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
DownloadVolumeFactor = downloadVolumeFactor,
|
||||
UploadVolumeFactor = uploadVolumeFactor,
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 172800, //48 hours
|
||||
MinimumSeedTime = 172800, // 48 hours
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.FileList
|
||||
{
|
||||
@@ -13,60 +15,74 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
var parameters = GetDefaultParameters();
|
||||
|
||||
if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace() || searchCriteria.SearchTerm.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
parameters.Add("action", "search-torrents");
|
||||
|
||||
if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
parameters.Add("type", "imdb");
|
||||
parameters.Add("query", searchCriteria.FullImdbId);
|
||||
}
|
||||
else if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
parameters.Add("type", "name");
|
||||
parameters.Add("query", searchCriteria.SanitizedSearchTerm.Trim());
|
||||
}
|
||||
|
||||
if (searchCriteria.Season.HasValue)
|
||||
{
|
||||
parameters.Add("season", searchCriteria.Season.ToString());
|
||||
parameters.Add("episode", searchCriteria.Episode);
|
||||
}
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetRequest(searchCriteria, parameters));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public virtual IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
var parameters = GetDefaultParameters();
|
||||
|
||||
if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
pageableRequests.Add(GetRequest("search-torrents", searchCriteria.Categories, string.Format("&type=imdb&query={0}", searchCriteria.FullImdbId)));
|
||||
parameters.Add("action", "search-torrents");
|
||||
parameters.Add("type", "imdb");
|
||||
parameters.Add("query", searchCriteria.FullImdbId);
|
||||
}
|
||||
else if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var titleYearSearchQuery = string.Format("{0}", searchCriteria.SanitizedSearchTerm);
|
||||
pageableRequests.Add(GetRequest("search-torrents", searchCriteria.Categories, string.Format("&type=name&query={0}", titleYearSearchQuery.Trim())));
|
||||
}
|
||||
else
|
||||
{
|
||||
pageableRequests.Add(GetRequest("latest-torrents", searchCriteria.Categories, ""));
|
||||
parameters.Add("action", "search-torrents");
|
||||
parameters.Add("type", "name");
|
||||
parameters.Add("query", searchCriteria.SanitizedSearchTerm.Trim());
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetRequest(searchCriteria, parameters));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
var parameters = GetDefaultParameters();
|
||||
|
||||
if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var titleYearSearchQuery = string.Format("{0}", searchCriteria.SanitizedSearchTerm);
|
||||
pageableRequests.Add(GetRequest("search-torrents", searchCriteria.Categories, string.Format("&type=name&query={0}", titleYearSearchQuery.Trim())));
|
||||
}
|
||||
else
|
||||
{
|
||||
pageableRequests.Add(GetRequest("latest-torrents", searchCriteria.Categories, ""));
|
||||
parameters.Add("action", "search-torrents");
|
||||
parameters.Add("type", "name");
|
||||
parameters.Add("query", searchCriteria.SanitizedSearchTerm.Trim());
|
||||
}
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
pageableRequests.Add(GetRequest("search-torrents", searchCriteria.Categories, string.Format("&type=imdb&query={0}&season={1}&episode={2}", searchCriteria.FullImdbId, searchCriteria.Season, searchCriteria.Episode)));
|
||||
}
|
||||
else if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var titleYearSearchQuery = string.Format("{0}", searchCriteria.SanitizedSearchTerm);
|
||||
pageableRequests.Add(GetRequest("search-torrents", searchCriteria.Categories, string.Format("&type=name&query={0}&season={1}&episode={2}", titleYearSearchQuery.Trim(), searchCriteria.Season, searchCriteria.Episode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
pageableRequests.Add(GetRequest("latest-torrents", searchCriteria.Categories, ""));
|
||||
}
|
||||
pageableRequests.Add(GetRequest(searchCriteria, parameters));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -74,47 +90,65 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
var parameters = GetDefaultParameters();
|
||||
|
||||
if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var titleYearSearchQuery = string.Format("{0}", searchCriteria.SanitizedSearchTerm);
|
||||
pageableRequests.Add(GetRequest("search-torrents", searchCriteria.Categories, string.Format("&type=name&query={0}", titleYearSearchQuery.Trim())));
|
||||
}
|
||||
else
|
||||
{
|
||||
pageableRequests.Add(GetRequest("latest-torrents", searchCriteria.Categories, ""));
|
||||
parameters.Add("action", "search-torrents");
|
||||
parameters.Add("type", "name");
|
||||
parameters.Add("query", searchCriteria.SanitizedSearchTerm.Trim());
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetRequest(searchCriteria, parameters));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
var parameters = GetDefaultParameters();
|
||||
|
||||
if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var titleYearSearchQuery = string.Format("{0}", searchCriteria.SanitizedSearchTerm);
|
||||
pageableRequests.Add(GetRequest("search-torrents", searchCriteria.Categories, string.Format("&type=name&query={0}", titleYearSearchQuery.Trim())));
|
||||
}
|
||||
else
|
||||
{
|
||||
pageableRequests.Add(GetRequest("latest-torrents", searchCriteria.Categories, ""));
|
||||
parameters.Add("action", "search-torrents");
|
||||
parameters.Add("type", "name");
|
||||
parameters.Add("query", searchCriteria.SanitizedSearchTerm.Trim());
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetRequest(searchCriteria, parameters));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetRequest(string searchType, int[] categories, string parameters)
|
||||
private IEnumerable<IndexerRequest> GetRequest(SearchCriteriaBase searchCriteria, NameValueCollection parameters)
|
||||
{
|
||||
var categoriesQuery = string.Join(",", Capabilities.Categories.MapTorznabCapsToTrackers(categories));
|
||||
if (parameters.Get("action") is null)
|
||||
{
|
||||
parameters.Add("action", "latest-torrents");
|
||||
}
|
||||
|
||||
var baseUrl = string.Format("{0}/api.php?action={1}&category={2}&username={3}&passkey={4}{5}", Settings.BaseUrl.TrimEnd('/'), searchType, categoriesQuery, Settings.Username.Trim(), Settings.Passkey.Trim(), parameters);
|
||||
parameters.Add("category", string.Join(",", Capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories)));
|
||||
|
||||
var searchUrl = $"{Settings.BaseUrl.TrimEnd('/')}/api.php?{parameters.GetQueryString()}";
|
||||
|
||||
yield return new IndexerRequest(searchUrl, HttpAccept.Json);
|
||||
}
|
||||
|
||||
private NameValueCollection GetDefaultParameters()
|
||||
{
|
||||
var parameters = new NameValueCollection
|
||||
{
|
||||
{ "username", Settings.Username.Trim() },
|
||||
{ "passkey", Settings.Passkey.Trim() }
|
||||
};
|
||||
|
||||
if (Settings.FreeleechOnly)
|
||||
{
|
||||
baseUrl += "&freeleech=1";
|
||||
parameters.Add("freeleech", "1");
|
||||
}
|
||||
|
||||
yield return new IndexerRequest(baseUrl, HttpAccept.Json);
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,7 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
|
||||
public class FileListSettings : NoAuthTorrentBaseSettings
|
||||
{
|
||||
private static readonly FileListSettingsValidator Validator = new FileListSettingsValidator();
|
||||
|
||||
public FileListSettings()
|
||||
{
|
||||
BaseUrl = "https://filelist.io";
|
||||
}
|
||||
private static readonly FileListSettingsValidator Validator = new ();
|
||||
|
||||
[FieldDefinition(2, Label = "Username", HelpText = "Site Username", Privacy = PrivacyLevel.UserName)]
|
||||
public string Username { get; set; }
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace NzbDrone.Core.Indexers.Definitions;
|
||||
public class GreatPosterWall : Gazelle.Gazelle
|
||||
{
|
||||
public override string Name => "GreatPosterWall";
|
||||
public override string[] IndexerUrls => new string[] { "https://greatposterwall.com/" };
|
||||
public override string[] IndexerUrls => new[] { "https://greatposterwall.com/" };
|
||||
public override string Description => "GreatPosterWall (GPW) is a CHINESE Private site for MOVIES";
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
|
||||
@@ -30,7 +30,7 @@ public class GreatPosterWall : Gazelle.Gazelle
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new GreatPosterWallRequestGenerator()
|
||||
return new GreatPosterWallRequestGenerator
|
||||
{
|
||||
Settings = Settings,
|
||||
HttpClient = _httpClient,
|
||||
@@ -81,8 +81,6 @@ public class GreatPosterWallRequestGenerator : GazelleRequestGenerator
|
||||
|
||||
public class GreatPosterWallParser : GazelleParser
|
||||
{
|
||||
private readonly HashSet<string> _hdResolutions = new HashSet<string> { "1080p", "1080i", "720p" };
|
||||
|
||||
public GreatPosterWallParser(GazelleSettings settings, IndexerCapabilities capabilities)
|
||||
: base(settings, capabilities)
|
||||
{
|
||||
@@ -121,7 +119,6 @@ public class GreatPosterWallParser : GazelleParser
|
||||
foreach (var torrent in result.Torrents)
|
||||
{
|
||||
var infoUrl = GetInfoUrl(result.GroupId.ToString(), torrent.TorrentId);
|
||||
|
||||
var time = DateTime.SpecifyKind(torrent.Time, DateTimeKind.Unspecified);
|
||||
|
||||
var release = new GazelleInfo
|
||||
@@ -134,7 +131,7 @@ public class GreatPosterWallParser : GazelleParser
|
||||
PosterUrl = GetPosterUrl(result.Cover),
|
||||
DownloadUrl = GetDownloadUrl(torrent.TorrentId, torrent.CanUseToken),
|
||||
PublishDate = new DateTimeOffset(time, TimeSpan.FromHours(8)).UtcDateTime, // Time is Chinese Time, add 8 hours difference from UTC
|
||||
Categories = ParseCategories(torrent),
|
||||
Categories = new List<IndexerCategory> { NewznabStandardCategory.Movies },
|
||||
Size = torrent.Size,
|
||||
Seeders = torrent.Seeders,
|
||||
Peers = torrent.Seeders + torrent.Leechers,
|
||||
@@ -181,7 +178,7 @@ public class GreatPosterWallParser : GazelleParser
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
protected string GetDownloadUrl(int torrentId, bool canUseToken)
|
||||
private string GetDownloadUrl(int torrentId, bool canUseToken)
|
||||
{
|
||||
var url = new HttpUri(_settings.BaseUrl)
|
||||
.CombinePath("/torrents.php")
|
||||
@@ -191,20 +188,6 @@ public class GreatPosterWallParser : GazelleParser
|
||||
|
||||
return url.FullUri;
|
||||
}
|
||||
|
||||
protected virtual List<IndexerCategory> ParseCategories(GreatPosterWallTorrent torrent)
|
||||
{
|
||||
var cats = new List<IndexerCategory>();
|
||||
|
||||
cats.Add(torrent.Resolution switch
|
||||
{
|
||||
var res when _hdResolutions.Contains(res) => NewznabStandardCategory.MoviesHD,
|
||||
"2160p" => NewznabStandardCategory.MoviesUHD,
|
||||
_ => NewznabStandardCategory.MoviesSD
|
||||
});
|
||||
|
||||
return cats;
|
||||
}
|
||||
}
|
||||
|
||||
public class GreatPosterWallResponse
|
||||
|
||||
@@ -7,10 +7,9 @@ using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Html.Parser;
|
||||
using FluentValidation;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Indexers.Settings;
|
||||
@@ -18,21 +17,20 @@ using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
public class HDSpace : TorrentIndexerBase<UserPassTorrentBaseSettings>
|
||||
{
|
||||
public override string Name => "HD-Space";
|
||||
public override string[] IndexerUrls => new string[] { "https://hd-space.org/" };
|
||||
private string LoginUrl => Settings.BaseUrl + "index.php?page=login";
|
||||
public override string[] IndexerUrls => new[] { "https://hd-space.org/" };
|
||||
public override string Description => "HD-Space (HDS) is a Private Torrent Tracker for HD MOVIES / TV";
|
||||
public override string Language => "en-US";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
private string LoginUrl => Settings.BaseUrl + "index.php?page=login";
|
||||
|
||||
public HDSpace(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
@@ -41,7 +39,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new HDSpaceRequestGenerator() { Settings = Settings, Capabilities = Capabilities };
|
||||
return new HDSpaceRequestGenerator { Settings = Settings, Capabilities = Capabilities };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
@@ -56,10 +54,10 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var requestBuilder = new HttpRequestBuilder(LoginUrl)
|
||||
{
|
||||
LogResponseContent = true,
|
||||
AllowAutoRedirect = true
|
||||
AllowAutoRedirect = true,
|
||||
Method = HttpMethod.Post
|
||||
};
|
||||
|
||||
requestBuilder.Method = HttpMethod.Post;
|
||||
requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15);
|
||||
|
||||
var cookies = Cookies;
|
||||
@@ -70,7 +68,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
.AddFormParameter("uid", Settings.Username)
|
||||
.AddFormParameter("pwd", Settings.Password)
|
||||
.SetCookies(loginPage.GetCookies())
|
||||
.SetHeader("Content-Type", "multipart/form-data")
|
||||
.SetHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||
.SetHeader("Referer", LoginUrl)
|
||||
.Build();
|
||||
|
||||
@@ -108,17 +106,17 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
|
||||
},
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
}
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(15, NewznabStandardCategory.MoviesBluRay, "Movie / Blu-ray");
|
||||
@@ -155,10 +153,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public UserPassTorrentBaseSettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
|
||||
public HDSpaceRequestGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories, string imdb = null)
|
||||
{
|
||||
var searchUrl = string.Format("{0}/index.php?page=torrents&", Settings.BaseUrl.TrimEnd('/'));
|
||||
@@ -249,7 +243,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
var resultParser = new HtmlParser();
|
||||
var searchResultDocument = resultParser.ParseDocument(indexerResponse.Content);
|
||||
var rows = searchResultDocument.QuerySelectorAll("table.lista > tbody > tr");
|
||||
var rows = searchResultDocument.QuerySelectorAll("div#bodyarea table.lista:not(:contains(\"Our Team Recommend\")) > tbody > tr:has(a[href^=\"index.php?page=torrent-details&id=\"])");
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
@@ -264,30 +258,46 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 86400; // 24 hours
|
||||
|
||||
var qLink = row.Children[1].FirstElementChild;
|
||||
release.Title = qLink.TextContent.Trim();
|
||||
release.InfoUrl = _settings.BaseUrl + qLink.GetAttribute("href");
|
||||
var qLink = row.QuerySelector("td:nth-child(2) a[href^=\"index.php?page=torrent-details&id=\"]");
|
||||
release.Title = qLink?.TextContent.Trim();
|
||||
release.InfoUrl = _settings.BaseUrl + qLink?.GetAttribute("href");
|
||||
release.Guid = release.InfoUrl;
|
||||
|
||||
var imdbLink = row.Children[1].QuerySelector("a[href*=imdb]");
|
||||
var downloadUrl = row.QuerySelector("td:nth-child(4) a[href^=\"download.php?id=\"]")?.GetAttribute("href");
|
||||
release.DownloadUrl = _settings.BaseUrl + downloadUrl;
|
||||
|
||||
// Use the torrent filename as release title
|
||||
var torrentTitle = ParseUtil.GetArgumentFromQueryString(downloadUrl, "f")?
|
||||
.Replace("&", "&")
|
||||
.Replace("'", "'")
|
||||
.Replace(".torrent", "")
|
||||
.Trim();
|
||||
|
||||
if (torrentTitle.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
release.Title = torrentTitle;
|
||||
}
|
||||
|
||||
var qGenres = row.QuerySelector("td:nth-child(2) span[style=\"color: #000000 \"]");
|
||||
if (qGenres != null)
|
||||
{
|
||||
var description = qGenres.TextContent.Split('\xA0').Last().Replace(" ", "");
|
||||
release.Description = description;
|
||||
release.Genres = description.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
}
|
||||
|
||||
var imdbLink = row.QuerySelector("td:nth-child(2) a[href*=imdb]");
|
||||
if (imdbLink != null)
|
||||
{
|
||||
release.ImdbId = ParseUtil.GetImdbID(imdbLink.GetAttribute("href").Split('/').Last()).GetValueOrDefault();
|
||||
}
|
||||
|
||||
var qDownload = row.Children[3].FirstElementChild;
|
||||
release.DownloadUrl = _settings.BaseUrl + qDownload.GetAttribute("href");
|
||||
|
||||
var dateStr = row.Children[4].TextContent.Trim();
|
||||
|
||||
//"July 11, 2015, 13:34:09", "Today|Yesterday at 20:04:23"
|
||||
release.PublishDate = DateTimeUtil.FromUnknown(dateStr);
|
||||
var sizeStr = row.Children[5].TextContent;
|
||||
release.Size = ParseUtil.GetBytes(sizeStr);
|
||||
release.Seeders = ParseUtil.CoerceInt(row.Children[7].TextContent);
|
||||
release.Peers = ParseUtil.CoerceInt(row.Children[8].TextContent) + release.Seeders;
|
||||
var grabs = row.QuerySelector("td:nth-child(10)").TextContent;
|
||||
grabs = grabs.Replace("---", "0");
|
||||
release.PublishDate = DateTimeUtil.FromUnknown(row.QuerySelector("td:nth-child(5)")?.TextContent.Trim());
|
||||
release.Size = ParseUtil.GetBytes(row.QuerySelector("td:nth-child(6)")?.TextContent.Trim());
|
||||
release.Seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(8)")?.TextContent);
|
||||
release.Peers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(9)")?.TextContent) + release.Seeders;
|
||||
var grabs = row.QuerySelector("td:nth-child(10)")?.TextContent.Trim().Replace("---", "0");
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (row.QuerySelector("img[title=\"FreeLeech\"]") != null)
|
||||
@@ -311,6 +321,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var qCat = row.QuerySelector("a[href^=\"index.php?page=torrents&category=\"]");
|
||||
var cat = qCat.GetAttribute("href").Split('=')[2];
|
||||
release.Categories = _categories.MapTrackerCatToNewznab(cat);
|
||||
|
||||
torrentInfos.Add(release);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,17 +7,14 @@ using System.Net.Http;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Html.Parser;
|
||||
using FluentValidation;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Annotations;
|
||||
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;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
@@ -25,12 +22,18 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
public override string Name => "HD-Torrents";
|
||||
|
||||
public override string[] IndexerUrls => new string[] { "https://hdts.ru/", "https://hd-torrents.org/" };
|
||||
public override string[] IndexerUrls => new[]
|
||||
{
|
||||
"https://hdts.ru/",
|
||||
"https://hd-torrents.org/",
|
||||
"https://hd-torrents.net/",
|
||||
"https://hd-torrents.me/",
|
||||
};
|
||||
public override string Description => "HD-Torrents is a private torrent website with HD torrents and strict rules on their content.";
|
||||
private string LoginUrl => Settings.BaseUrl + "login.php";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
private string LoginUrl => Settings.BaseUrl + "login.php";
|
||||
|
||||
public HDTorrents(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
@@ -39,7 +42,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new HDTorrentsRequestGenerator() { Settings = Settings, Capabilities = Capabilities };
|
||||
return new HDTorrentsRequestGenerator { Settings = Settings, Capabilities = Capabilities };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
@@ -51,10 +54,10 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var requestBuilder = new HttpRequestBuilder(LoginUrl)
|
||||
{
|
||||
LogResponseContent = true
|
||||
LogResponseContent = true,
|
||||
Method = HttpMethod.Post
|
||||
};
|
||||
|
||||
requestBuilder.Method = HttpMethod.Post;
|
||||
requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15);
|
||||
|
||||
var cookies = Cookies;
|
||||
@@ -63,7 +66,8 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var authLoginRequest = requestBuilder
|
||||
.AddFormParameter("uid", Settings.Username)
|
||||
.AddFormParameter("pwd", Settings.Password)
|
||||
.SetHeader("Content-Type", "multipart/form-data")
|
||||
.SetHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||
.SetHeader("Referer", LoginUrl)
|
||||
.Build();
|
||||
|
||||
var response = await ExecuteAuth(authLoginRequest);
|
||||
@@ -76,12 +80,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
|
||||
{
|
||||
if (httpResponse.Content.Contains("Error:You're not authorized"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return httpResponse.Content.Contains("Error:You're not authorized");
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
@@ -89,21 +88,25 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
|
||||
},
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
}
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
Flags = new List<IndexerFlag>
|
||||
{
|
||||
IndexerFlag.Internal
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping("70", NewznabStandardCategory.MoviesUHD, "Movie/UHD/Blu-Ray");
|
||||
caps.Categories.AddCategoryMapping("1", NewznabStandardCategory.MoviesHD, "Movie/Blu-Ray");
|
||||
caps.Categories.AddCategoryMapping("70", NewznabStandardCategory.MoviesBluRay, "Movie/UHD/Blu-Ray");
|
||||
caps.Categories.AddCategoryMapping("1", NewznabStandardCategory.MoviesBluRay, "Movie/Blu-Ray");
|
||||
caps.Categories.AddCategoryMapping("71", NewznabStandardCategory.MoviesUHD, "Movie/UHD/Remux");
|
||||
caps.Categories.AddCategoryMapping("2", NewznabStandardCategory.MoviesHD, "Movie/Remux");
|
||||
caps.Categories.AddCategoryMapping("5", NewznabStandardCategory.MoviesHD, "Movie/1080p/i");
|
||||
@@ -144,10 +147,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public UserPassTorrentBaseSettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
|
||||
public HDTorrentsRequestGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories, string imdbId = null)
|
||||
{
|
||||
var searchUrl = Settings.BaseUrl + "torrents.php?" + string.Join(string.Empty, Capabilities.Categories.MapTorznabCapsToTrackers(categories).Select(cat => $"category[]={cat}&"));
|
||||
@@ -224,7 +223,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
|
||||
private readonly Regex _posterRegex = new Regex(@"src=\\'./([^']+)\\'", RegexOptions.IgnoreCase);
|
||||
private readonly HashSet<string> _freeleechRanks = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
private readonly HashSet<string> _freeleechRanks = new (StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"VIP",
|
||||
"Uploader",
|
||||
@@ -248,7 +247,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var dom = parser.ParseDocument(indexerResponse.Content);
|
||||
|
||||
var userInfo = dom.QuerySelector("table.navus tr");
|
||||
var userRank = userInfo.Children[1].TextContent.Replace("Rank:", string.Empty).Trim();
|
||||
var userRank = userInfo?.Children[1].TextContent.Replace("Rank:", string.Empty).Trim();
|
||||
var hasFreeleech = _freeleechRanks.Contains(userRank);
|
||||
|
||||
var rows = dom.QuerySelectorAll("table.mainblockcontenttt tr:has(td.mainblockcontent)");
|
||||
@@ -259,7 +258,9 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var details = new Uri(_settings.BaseUrl + mainLink.GetAttribute("href"));
|
||||
|
||||
var posterMatch = _posterRegex.Match(mainLink.GetAttribute("onmouseover"));
|
||||
var poster = posterMatch.Success ? _settings.BaseUrl + posterMatch.Groups[1].Value.Replace("\\", "/") : null;
|
||||
var poster = posterMatch.Success
|
||||
? _settings.BaseUrl + posterMatch.Groups[1].Value.Replace("\\", "/")
|
||||
: null;
|
||||
|
||||
var link = new Uri(_settings.BaseUrl + row.Children[4].FirstElementChild.GetAttribute("href"));
|
||||
var description = row.Children[2].QuerySelector("span").TextContent;
|
||||
@@ -329,6 +330,13 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var imdbLink = row.QuerySelector("a[href*=\"www.imdb.com/title/\"]")?.GetAttribute("href");
|
||||
var imdb = !string.IsNullOrWhiteSpace(imdbLink) ? ParseUtil.GetImdbID(imdbLink) : null;
|
||||
|
||||
var flags = new HashSet<IndexerFlag>();
|
||||
|
||||
if (row.QuerySelector("img[src$=\"internal.png\"]") != null)
|
||||
{
|
||||
flags.Add(IndexerFlag.Internal);
|
||||
}
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Title = title,
|
||||
@@ -343,6 +351,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
Grabs = grabs,
|
||||
Seeders = seeders,
|
||||
Peers = peers,
|
||||
IndexerFlags = flags,
|
||||
DownloadVolumeFactor = dlVolumeFactor,
|
||||
UploadVolumeFactor = upVolumeFactor,
|
||||
MinimumRatio = 1,
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
public override string Name => "IPTorrents";
|
||||
|
||||
public override string[] IndexerUrls => new string[]
|
||||
public override string[] IndexerUrls => new[]
|
||||
{
|
||||
"https://iptorrents.com/",
|
||||
"https://iptorrents.me/",
|
||||
@@ -46,7 +46,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new IPTorrentsRequestGenerator() { Settings = Settings, Capabilities = Capabilities };
|
||||
return new IPTorrentsRequestGenerator { Settings = Settings, Capabilities = Capabilities };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
@@ -64,21 +64,21 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
|
||||
},
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(72, NewznabStandardCategory.Movies, "Movies");
|
||||
@@ -161,10 +161,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public IPTorrentsSettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
|
||||
public IPTorrentsRequestGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories, int limit, int offset, string imdbId = null)
|
||||
{
|
||||
var searchUrl = Settings.BaseUrl + "t";
|
||||
@@ -194,9 +190,16 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
qc.Add(cat, string.Empty);
|
||||
}
|
||||
|
||||
qc.Add("p", ((offset / limit) + 1).ToString());
|
||||
if (offset > 0 && limit > 0)
|
||||
{
|
||||
var page = (int)(offset / limit) + 1;
|
||||
qc.Add("p", page.ToString());
|
||||
}
|
||||
|
||||
searchUrl = searchUrl + "?" + qc.GetQueryString();
|
||||
if (qc.Count > 0)
|
||||
{
|
||||
searchUrl += $"?{qc.GetQueryString()}";
|
||||
}
|
||||
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Html);
|
||||
|
||||
@@ -356,14 +359,10 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public class IPTorrentsSettings : CookieTorrentBaseSettings
|
||||
{
|
||||
public IPTorrentsSettings()
|
||||
{
|
||||
}
|
||||
|
||||
[FieldDefinition(2, Label = "Cookie User-Agent", Type = FieldType.Textbox, HelpText = "User-Agent associated with cookie used from Browser")]
|
||||
public string UserAgent { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "FreeLeech Only", Type = FieldType.Checkbox, Advanced = true, HelpText = "Search Freeleech torrents only")]
|
||||
[FieldDefinition(3, Label = "FreeLeech Only", Type = FieldType.Checkbox, HelpText = "Search Freeleech torrents only")]
|
||||
public bool FreeLeechOnly { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Html.Parser;
|
||||
using FluentValidation;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Indexers.Settings;
|
||||
@@ -18,21 +16,18 @@ 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
|
||||
{
|
||||
public class ImmortalSeed : TorrentIndexerBase<UserPassTorrentBaseSettings>
|
||||
{
|
||||
public override string Name => "ImmortalSeed";
|
||||
|
||||
public override string[] IndexerUrls => new string[] { "https://immortalseed.me/" };
|
||||
public override string[] IndexerUrls => new[] { "https://immortalseed.me/" };
|
||||
public override string Description => "ImmortalSeed (iS) is a Private Torrent Tracker for MOVIES / TV / GENERAL";
|
||||
private string LoginUrl => Settings.BaseUrl + "takelogin.php";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
private string LoginUrl => Settings.BaseUrl + "takelogin.php";
|
||||
|
||||
public ImmortalSeed(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
@@ -41,7 +36,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new ImmortalSeedRequestGenerator() { Settings = Settings, Capabilities = Capabilities };
|
||||
return new ImmortalSeedRequestGenerator { Settings = Settings, Capabilities = Capabilities };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
@@ -53,7 +48,8 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var requestBuilder = new HttpRequestBuilder(LoginUrl)
|
||||
{
|
||||
LogResponseContent = true
|
||||
LogResponseContent = true,
|
||||
AllowAutoRedirect = true
|
||||
};
|
||||
|
||||
requestBuilder.Method = HttpMethod.Post;
|
||||
@@ -65,12 +61,12 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var authLoginRequest = requestBuilder
|
||||
.AddFormParameter("username", Settings.Username)
|
||||
.AddFormParameter("password", Settings.Password)
|
||||
.SetHeader("Content-Type", "multipart/form-data")
|
||||
.SetHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||
.Build();
|
||||
|
||||
var response = await ExecuteAuth(authLoginRequest);
|
||||
|
||||
if (!response.Content.Contains("You have successfully logged in"))
|
||||
if (!response.Content.Contains("logout.php"))
|
||||
{
|
||||
throw new IndexerAuthException("ImmortalSeed Auth Failed");
|
||||
}
|
||||
@@ -83,12 +79,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
|
||||
{
|
||||
if (httpResponse.Content.Contains("You do not have permission to access this page."))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return httpResponse.Content.Contains("You do not have permission to access this page.");
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
@@ -96,21 +87,21 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q
|
||||
},
|
||||
{
|
||||
MovieSearchParam.Q
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.Other, "Nuked");
|
||||
@@ -119,42 +110,45 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
caps.Categories.AddCategoryMapping(35, NewznabStandardCategory.AudioAudiobook, "Audiobooks");
|
||||
caps.Categories.AddCategoryMapping(31, NewznabStandardCategory.TV, "Childrens/Cartoons");
|
||||
caps.Categories.AddCategoryMapping(54, NewznabStandardCategory.TVDocumentary, "Documentary - HD");
|
||||
caps.Categories.AddCategoryMapping(41, NewznabStandardCategory.BooksComics, "Comics");
|
||||
caps.Categories.AddCategoryMapping(53, NewznabStandardCategory.TVDocumentary, "Documentary - SD");
|
||||
caps.Categories.AddCategoryMapping(22, NewznabStandardCategory.BooksEBook, "Ebooks");
|
||||
caps.Categories.AddCategoryMapping(41, NewznabStandardCategory.BooksComics, "Ebooks -- Comics");
|
||||
caps.Categories.AddCategoryMapping(46, NewznabStandardCategory.BooksMags, "Ebooks -- Magazines");
|
||||
caps.Categories.AddCategoryMapping(25, NewznabStandardCategory.PCGames, "Games");
|
||||
caps.Categories.AddCategoryMapping(29, NewznabStandardCategory.ConsoleXBox, "Games Xbox");
|
||||
caps.Categories.AddCategoryMapping(27, NewznabStandardCategory.PCGames, "Games-PC Rips");
|
||||
caps.Categories.AddCategoryMapping(28, NewznabStandardCategory.ConsolePS4, "Games-PSx");
|
||||
caps.Categories.AddCategoryMapping(61, NewznabStandardCategory.ConsoleNDS, "Games -- Nintendo");
|
||||
caps.Categories.AddCategoryMapping(26, NewznabStandardCategory.PCGames, "Games -- PC");
|
||||
caps.Categories.AddCategoryMapping(28, NewznabStandardCategory.ConsolePS3, "Games -- Playstation");
|
||||
caps.Categories.AddCategoryMapping(29, NewznabStandardCategory.ConsoleXBox, "Games -- Xbox");
|
||||
caps.Categories.AddCategoryMapping(49, NewznabStandardCategory.PCMobileOther, "Mobile");
|
||||
caps.Categories.AddCategoryMapping(51, NewznabStandardCategory.PCMobileAndroid, "Mobile -- Android");
|
||||
caps.Categories.AddCategoryMapping(50, NewznabStandardCategory.PCMobileiOS, "Mobile -- IOS");
|
||||
caps.Categories.AddCategoryMapping(52, NewznabStandardCategory.PCMobileOther, "Mobile -- Windows");
|
||||
caps.Categories.AddCategoryMapping(59, NewznabStandardCategory.MoviesUHD, "Movies-4k");
|
||||
caps.Categories.AddCategoryMapping(60, NewznabStandardCategory.MoviesForeign, "Non-English 4k Movies");
|
||||
caps.Categories.AddCategoryMapping(16, NewznabStandardCategory.MoviesHD, "Movies HD");
|
||||
caps.Categories.AddCategoryMapping(18, NewznabStandardCategory.MoviesForeign, "Movies HD Non-English");
|
||||
caps.Categories.AddCategoryMapping(17, NewznabStandardCategory.MoviesSD, "TS/CAM/PPV");
|
||||
caps.Categories.AddCategoryMapping(34, NewznabStandardCategory.MoviesForeign, "Movies Low Def Non-English");
|
||||
caps.Categories.AddCategoryMapping(60, NewznabStandardCategory.MoviesForeign, "Movies-4k -- Non-English");
|
||||
caps.Categories.AddCategoryMapping(16, NewznabStandardCategory.MoviesHD, "Movies-HD");
|
||||
caps.Categories.AddCategoryMapping(18, NewznabStandardCategory.MoviesForeign, "Movies-HD -- Non-English");
|
||||
caps.Categories.AddCategoryMapping(17, NewznabStandardCategory.MoviesSD, "Movies-Low Def");
|
||||
caps.Categories.AddCategoryMapping(34, NewznabStandardCategory.MoviesForeign, "Movies-Low Def -- Non-English");
|
||||
caps.Categories.AddCategoryMapping(62, NewznabStandardCategory.Movies, "Movies-Packs");
|
||||
caps.Categories.AddCategoryMapping(14, NewznabStandardCategory.MoviesSD, "Movies-SD");
|
||||
caps.Categories.AddCategoryMapping(33, NewznabStandardCategory.MoviesForeign, "Movies SD Non-English");
|
||||
caps.Categories.AddCategoryMapping(33, NewznabStandardCategory.MoviesForeign, "Movies-SD -- Non-English");
|
||||
caps.Categories.AddCategoryMapping(30, NewznabStandardCategory.AudioOther, "Music");
|
||||
caps.Categories.AddCategoryMapping(37, NewznabStandardCategory.AudioLossless, "FLAC");
|
||||
caps.Categories.AddCategoryMapping(36, NewznabStandardCategory.AudioMP3, "MP3");
|
||||
caps.Categories.AddCategoryMapping(39, NewznabStandardCategory.AudioOther, "Music Other");
|
||||
caps.Categories.AddCategoryMapping(38, NewznabStandardCategory.AudioVideo, "Music Video");
|
||||
caps.Categories.AddCategoryMapping(37, NewznabStandardCategory.AudioLossless, "Music -- FLAC");
|
||||
caps.Categories.AddCategoryMapping(36, NewznabStandardCategory.AudioMP3, "Music -- MP3");
|
||||
caps.Categories.AddCategoryMapping(39, NewznabStandardCategory.AudioOther, "Music -- Other");
|
||||
caps.Categories.AddCategoryMapping(38, NewznabStandardCategory.AudioVideo, "Music -- Video");
|
||||
caps.Categories.AddCategoryMapping(45, NewznabStandardCategory.Other, "Other");
|
||||
caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.TVSport, "Sports Tv");
|
||||
caps.Categories.AddCategoryMapping(44, NewznabStandardCategory.TVSport, "Sports Fitness-Instructional");
|
||||
caps.Categories.AddCategoryMapping(58, NewznabStandardCategory.TVSport, "Olympics");
|
||||
caps.Categories.AddCategoryMapping(44, NewznabStandardCategory.TVSport, "Sports Tv -- Fitness-Instructional");
|
||||
caps.Categories.AddCategoryMapping(58, NewznabStandardCategory.TVSport, "Sports Tv -- Olympics");
|
||||
caps.Categories.AddCategoryMapping(47, NewznabStandardCategory.TVSD, "TV - 480p");
|
||||
caps.Categories.AddCategoryMapping(64, NewznabStandardCategory.TVUHD, "TV - 4K");
|
||||
caps.Categories.AddCategoryMapping(8, NewznabStandardCategory.TVHD, "TV - High Definition");
|
||||
caps.Categories.AddCategoryMapping(48, NewznabStandardCategory.TVSD, "TV - Standard Definition - x264");
|
||||
caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.TVSD, "TV - Standard Definition - XviD");
|
||||
caps.Categories.AddCategoryMapping(48, NewznabStandardCategory.TVSD, "TV SD - x264");
|
||||
caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.TVSD, "TV SD - XviD");
|
||||
caps.Categories.AddCategoryMapping(63, NewznabStandardCategory.TVUHD, "TV Season Packs - 4K");
|
||||
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.TVHD, "TV Season Packs - HD");
|
||||
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.TVSD, "TV Season Packs - SD");
|
||||
caps.Categories.AddCategoryMapping(22, NewznabStandardCategory.BooksEBook, "Ebooks");
|
||||
caps.Categories.AddCategoryMapping(26, NewznabStandardCategory.PCGames, "Games-PC ISO");
|
||||
caps.Categories.AddCategoryMapping(46, NewznabStandardCategory.BooksMags, "Magazines");
|
||||
caps.Categories.AddCategoryMapping(50, NewznabStandardCategory.PCMobileiOS, "IOS");
|
||||
caps.Categories.AddCategoryMapping(51, NewznabStandardCategory.PCMobileAndroid, "Android");
|
||||
caps.Categories.AddCategoryMapping(52, NewznabStandardCategory.PC0day, "Windows");
|
||||
caps.Categories.AddCategoryMapping(53, NewznabStandardCategory.TVDocumentary, "Documentary - SD");
|
||||
|
||||
return caps;
|
||||
}
|
||||
@@ -165,27 +159,33 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public UserPassTorrentBaseSettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
|
||||
public ImmortalSeedRequestGenerator()
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
}
|
||||
var parameters = new NameValueCollection();
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories, string imdbId = null)
|
||||
{
|
||||
var searchUrl = Settings.BaseUrl + "browse.php?";
|
||||
var term = searchCriteria.SanitizedSearchTerm;
|
||||
|
||||
if (term.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
searchUrl += string.Format("do=search&keywords={0}&search_type=t_name&category=0&include_dead_torrents=no", WebUtility.UrlEncode(term));
|
||||
parameters.Add("do", "search");
|
||||
parameters.Add("keywords", term.Trim());
|
||||
parameters.Add("search_type", "t_name");
|
||||
parameters.Add("category", "0");
|
||||
parameters.Add("include_dead_torrents", "no");
|
||||
}
|
||||
|
||||
if (categories != null && categories.Length > 0)
|
||||
{
|
||||
if (term.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
searchUrl += "&";
|
||||
}
|
||||
var queryCats = Capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories);
|
||||
|
||||
searchUrl += "selectedcats2=" + string.Join(",", Capabilities.Categories.MapTorznabCapsToTrackers(categories));
|
||||
if (queryCats.Count > 0)
|
||||
{
|
||||
parameters.Add("selectedcats2", string.Join(",", queryCats));
|
||||
}
|
||||
|
||||
var searchUrl = Settings.BaseUrl + "browse.php";
|
||||
|
||||
if (parameters.Count > 0)
|
||||
{
|
||||
searchUrl += $"?{parameters.GetQueryString()}";
|
||||
}
|
||||
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Html);
|
||||
@@ -196,8 +196,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -205,8 +204,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -214,8 +212,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -223,8 +220,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -232,8 +228,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ using System.Threading.Tasks;
|
||||
using FluentValidation;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Annotations;
|
||||
@@ -25,29 +27,30 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
public class MyAnonamouse : TorrentIndexerBase<MyAnonamouseSettings>
|
||||
{
|
||||
private static readonly Regex TorrentIdRegex = new Regex(@"tor/download.php\?tid=(?<id>\d+)$");
|
||||
|
||||
public override string Name => "MyAnonamouse";
|
||||
|
||||
public override string[] IndexerUrls => new string[] { "https://www.myanonamouse.net/" };
|
||||
public override string[] IndexerUrls => new[] { "https://www.myanonamouse.net/" };
|
||||
public override string Description => "MyAnonaMouse (MAM) is a large ebook and audiobook tracker.";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override int PageSize => 100;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
private readonly ICacheManager _cacheManager;
|
||||
private static readonly Regex TorrentIdRegex = new Regex(@"tor/download.php\?tid=(?<id>\d+)$");
|
||||
|
||||
public MyAnonamouse(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
public MyAnonamouse(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger, ICacheManager cacheManager)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
_cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new MyAnonamouseRequestGenerator() { Settings = Settings, Capabilities = Capabilities };
|
||||
return new MyAnonamouseRequestGenerator { Settings = Settings, Capabilities = Capabilities };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new MyAnonamouseParser(Settings, Capabilities.Categories);
|
||||
return new MyAnonamouseParser(Settings, Capabilities.Categories, _httpClient, _cacheManager, _logger);
|
||||
}
|
||||
|
||||
public override async Task<byte[]> Download(Uri link)
|
||||
@@ -71,7 +74,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
var indexerReq = new IndexerRequest(freeleechRequest);
|
||||
var response = await FetchIndexerResponse(indexerReq).ConfigureAwait(false);
|
||||
var resource = Json.Deserialize<MyAnonamouseFreeleechResponse>(response.Content);
|
||||
var resource = Json.Deserialize<MyAnonamouseBuyPersonalFreeleechResponse>(response.Content);
|
||||
|
||||
if (resource.Success)
|
||||
{
|
||||
@@ -101,9 +104,9 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping("13", NewznabStandardCategory.AudioAudiobook, "AudioBooks");
|
||||
@@ -210,84 +213,97 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public MyAnonamouseSettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
|
||||
public MyAnonamouseRequestGenerator()
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
}
|
||||
var term = searchCriteria.SanitizedSearchTerm.Trim();
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories)
|
||||
{
|
||||
var qParams = new NameValueCollection
|
||||
var searchType = Settings.SearchType switch
|
||||
{
|
||||
{ "tor[text]", term },
|
||||
{ "tor[srchIn][title]", "true" },
|
||||
{ "tor[srchIn][author]", "true" },
|
||||
{ "tor[searchType]", Settings.ExcludeVip ? "nVIP" : "all" }, // exclude VIP torrents
|
||||
{ "tor[searchIn]", "torrents" },
|
||||
{ "tor[hash]", "" },
|
||||
{ "tor[sortType]", "default" },
|
||||
{ "tor[startNumber]", "0" },
|
||||
{ "thumbnails", "1" }, // gives links for thumbnail sized versions of their posters
|
||||
|
||||
//{ "posterLink", "1"}, // gives links for a full sized poster
|
||||
//{ "dlLink", "1"}, // include the url to download the torrent
|
||||
{ "description", "1" } // include the description
|
||||
|
||||
//{"bookmarks", "0"} // include if the item is bookmarked or not
|
||||
(int)MyAnonamouseSearchType.Active => "active",
|
||||
(int)MyAnonamouseSearchType.Freeleech => "fl",
|
||||
(int)MyAnonamouseSearchType.FreeleechOrVip => "fl-VIP",
|
||||
(int)MyAnonamouseSearchType.Vip => "VIP",
|
||||
(int)MyAnonamouseSearchType.NotVip => "nVIP",
|
||||
_ => "all"
|
||||
};
|
||||
|
||||
var catList = Capabilities.Categories.MapTorznabCapsToTrackers(categories);
|
||||
var parameters = new NameValueCollection
|
||||
{
|
||||
{ "tor[text]", term },
|
||||
{ "tor[searchType]", searchType },
|
||||
{ "tor[srchIn][title]", "true" },
|
||||
{ "tor[srchIn][author]", "true" },
|
||||
{ "tor[srchIn][narrator]", "true" },
|
||||
{ "tor[searchIn]", "torrents" },
|
||||
{ "tor[sortType]", "default" },
|
||||
{ "tor[perpage]", searchCriteria.Limit?.ToString() ?? "100" },
|
||||
{ "tor[startNumber]", searchCriteria.Offset?.ToString() ?? "0" },
|
||||
{ "thumbnails", "1" }, // gives links for thumbnail sized versions of their posters
|
||||
{ "description", "1" } // include the description
|
||||
};
|
||||
|
||||
if (Settings.SearchInDescription)
|
||||
{
|
||||
parameters.Add("tor[srchIn][description]", "true");
|
||||
}
|
||||
|
||||
if (Settings.SearchInSeries)
|
||||
{
|
||||
parameters.Add("tor[srchIn][series]", "true");
|
||||
}
|
||||
|
||||
if (Settings.SearchInFilenames)
|
||||
{
|
||||
parameters.Add("tor[srchIn][filenames]", "true");
|
||||
}
|
||||
|
||||
var catList = Capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories);
|
||||
if (catList.Any())
|
||||
{
|
||||
var index = 0;
|
||||
foreach (var cat in catList)
|
||||
{
|
||||
qParams.Add("tor[cat][" + index + "]", cat);
|
||||
parameters.Add("tor[cat][" + index + "]", cat);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qParams.Add("tor[cat][]", "0");
|
||||
parameters.Add("tor[cat][]", "0");
|
||||
}
|
||||
|
||||
var urlSearch = Settings.BaseUrl + "tor/js/loadSearchJSONbasic.php";
|
||||
var searchUrl = Settings.BaseUrl + "tor/js/loadSearchJSONbasic.php";
|
||||
|
||||
if (qParams.Count > 0)
|
||||
if (parameters.Count > 0)
|
||||
{
|
||||
urlSearch += $"?{qParams.GetQueryString()}";
|
||||
searchUrl += $"?{parameters.GetQueryString()}";
|
||||
}
|
||||
|
||||
var request = new IndexerRequest(urlSearch, HttpAccept.Json);
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Json);
|
||||
|
||||
yield return request;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
return pageableRequests;
|
||||
return new IndexerPageableRequestChain();
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
return pageableRequests;
|
||||
return new IndexerPageableRequestChain();
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
return pageableRequests;
|
||||
return new IndexerPageableRequestChain();
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -296,7 +312,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -309,11 +325,26 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
private readonly MyAnonamouseSettings _settings;
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
private readonly IIndexerHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
private readonly ICached<string> _userClassCache;
|
||||
private readonly HashSet<string> _vipFreeleechUserClasses = new (StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"VIP",
|
||||
"Elite VIP",
|
||||
};
|
||||
|
||||
public MyAnonamouseParser(MyAnonamouseSettings settings, IndexerCapabilitiesCategories categories)
|
||||
public MyAnonamouseParser(MyAnonamouseSettings settings,
|
||||
IndexerCapabilitiesCategories categories,
|
||||
IIndexerHttpClient httpClient,
|
||||
ICacheManager cacheManager,
|
||||
Logger logger)
|
||||
{
|
||||
_settings = settings;
|
||||
_categories = categories;
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
_userClassCache = cacheManager.GetCache<string>(GetType());
|
||||
}
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
@@ -321,7 +352,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
// Throw auth errors here before we try to parse
|
||||
if (indexerResponse.HttpResponse.StatusCode == HttpStatusCode.Forbidden)
|
||||
{
|
||||
throw new IndexerAuthException("[403 Forbidden] - mam_session_id expired or invalid");
|
||||
throw new IndexerAuthException("[403 Forbidden] - mam_id expired or invalid");
|
||||
}
|
||||
|
||||
// Throw common http errors here before we try to parse
|
||||
@@ -351,45 +382,46 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
return torrentInfos.ToArray();
|
||||
}
|
||||
|
||||
var hasUserVip = HasUserVip();
|
||||
|
||||
foreach (var item in jsonResponse.Data)
|
||||
{
|
||||
//TODO shift to ReleaseInfo object initializer for consistency
|
||||
var release = new TorrentInfo();
|
||||
|
||||
var id = item.Id;
|
||||
release.Title = item.Title;
|
||||
|
||||
// release.Description = item.Value<string>("description");
|
||||
var author = string.Empty;
|
||||
release.Title = item.Title;
|
||||
release.Description = item.Description;
|
||||
|
||||
if (item.AuthorInfo != null)
|
||||
{
|
||||
var authorInfo = JsonConvert.DeserializeObject<Dictionary<string, string>>(item.AuthorInfo);
|
||||
author = authorInfo?.First().Value;
|
||||
}
|
||||
var author = authorInfo?.Take(5).Select(v => v.Value).Join(", ");
|
||||
|
||||
if (author != null)
|
||||
{
|
||||
release.Title += " by " + author;
|
||||
if (author.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
release.Title += " by " + author;
|
||||
}
|
||||
}
|
||||
|
||||
var flags = new List<string>();
|
||||
|
||||
var langCode = item.LangCode;
|
||||
if (!string.IsNullOrEmpty(langCode))
|
||||
var languageCode = item.LanguageCode;
|
||||
if (!string.IsNullOrEmpty(languageCode))
|
||||
{
|
||||
flags.Add(langCode);
|
||||
flags.Add(languageCode);
|
||||
}
|
||||
|
||||
var filetype = item.Filetype;
|
||||
if (!string.IsNullOrEmpty(filetype))
|
||||
{
|
||||
flags.Add(filetype);
|
||||
flags.Add(filetype.ToUpper());
|
||||
}
|
||||
|
||||
if (flags.Count > 0)
|
||||
{
|
||||
release.Title += " [" + string.Join(" / ", flags) + "]";
|
||||
release.Title += " [" + flags.Join(" / ") + "]";
|
||||
}
|
||||
|
||||
if (item.Vip)
|
||||
@@ -397,26 +429,20 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
release.Title += " [VIP]";
|
||||
}
|
||||
|
||||
var category = item.Category;
|
||||
release.Categories = _categories.MapTrackerCatToNewznab(category);
|
||||
|
||||
release.DownloadUrl = _settings.BaseUrl + "/tor/download.php?tid=" + id;
|
||||
release.InfoUrl = _settings.BaseUrl + "/t/" + id;
|
||||
release.DownloadUrl = _settings.BaseUrl + "tor/download.php?tid=" + id;
|
||||
release.InfoUrl = _settings.BaseUrl + "t/" + id;
|
||||
release.Guid = release.InfoUrl;
|
||||
|
||||
var dateStr = item.Added;
|
||||
var dateTime = DateTime.ParseExact(dateStr, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
|
||||
release.PublishDate = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc).ToLocalTime();
|
||||
|
||||
release.Categories = _categories.MapTrackerCatToNewznab(item.Category);
|
||||
release.PublishDate = DateTime.ParseExact(item.Added, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime();
|
||||
release.Grabs = item.Grabs;
|
||||
release.Files = item.NumFiles;
|
||||
release.Seeders = item.Seeders;
|
||||
release.Peers = item.Leechers + release.Seeders;
|
||||
var size = item.Size;
|
||||
release.Size = ParseUtil.GetBytes(size);
|
||||
|
||||
release.DownloadVolumeFactor = item.Free ? 0 : 1;
|
||||
release.Size = ParseUtil.GetBytes(item.Size);
|
||||
release.DownloadVolumeFactor = item.Free ? 0 : hasUserVip && item.FreeVip ? 0 : 1;
|
||||
release.UploadVolumeFactor = 1;
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 259200; // 72 hours
|
||||
|
||||
torrentInfos.Add(release);
|
||||
}
|
||||
@@ -424,6 +450,33 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
return torrentInfos.ToArray();
|
||||
}
|
||||
|
||||
private bool HasUserVip()
|
||||
{
|
||||
var cacheKey = "myanonamouse_user_class_" + _settings.ToJson().SHA256Hash();
|
||||
|
||||
var userClass = _userClassCache.Get(
|
||||
cacheKey,
|
||||
() =>
|
||||
{
|
||||
var request = new HttpRequestBuilder(_settings.BaseUrl.Trim('/'))
|
||||
.Resource("/jsonLoad.php")
|
||||
.Accept(HttpAccept.Json)
|
||||
.Build();
|
||||
|
||||
_logger.Debug("Fetching user data: " + request.Url.FullUri);
|
||||
|
||||
request.Cookies.Add("mam_id", _settings.MamId);
|
||||
|
||||
var response = _httpClient.Get(request);
|
||||
var jsonResponse = JsonConvert.DeserializeObject<MyAnonamouseUserDataResponse>(response.Content);
|
||||
|
||||
return jsonResponse.UserClass?.Trim();
|
||||
},
|
||||
TimeSpan.FromHours(1));
|
||||
|
||||
return _vipFreeleechUserClasses.Contains(userClass);
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
@@ -442,39 +495,68 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public MyAnonamouseSettings()
|
||||
{
|
||||
MamId = "";
|
||||
SearchType = (int)MyAnonamouseSearchType.All;
|
||||
SearchInDescription = false;
|
||||
SearchInSeries = false;
|
||||
SearchInFilenames = false;
|
||||
}
|
||||
|
||||
[FieldDefinition(2, Label = "Mam Id", HelpText = "Mam Session Id (Created Under Preferences -> Security)")]
|
||||
[FieldDefinition(2, Type = FieldType.Textbox, Label = "Mam Id", HelpText = "Mam Session Id (Created Under Preferences -> Security)")]
|
||||
public string MamId { get; set; }
|
||||
|
||||
[FieldDefinition(3, Type = FieldType.Checkbox, Label = "Exclude VIP", HelpText = "Exclude VIP Torrents from search results")]
|
||||
public bool ExcludeVip { get; set; }
|
||||
[FieldDefinition(3, Type = FieldType.Select, Label = "Search Type", SelectOptions = typeof(MyAnonamouseSearchType), HelpText = "Specify the desired search type")]
|
||||
public int SearchType { get; set; }
|
||||
|
||||
[FieldDefinition(4, Type = FieldType.Checkbox, Label = "Freeleech", HelpText = "Use freeleech token for download")]
|
||||
[FieldDefinition(4, Type = FieldType.Checkbox, Label = "Buy Freeleech Token", HelpText = "Buy personal freeleech token for download")]
|
||||
public bool Freeleech { get; set; }
|
||||
|
||||
[FieldDefinition(5, Type = FieldType.Checkbox, Label = "Search in description", HelpText = "Search text in the description")]
|
||||
public bool SearchInDescription { get; set; }
|
||||
|
||||
[FieldDefinition(6, Type = FieldType.Checkbox, Label = "Search in series", HelpText = "Search text in the series")]
|
||||
public bool SearchInSeries { get; set; }
|
||||
|
||||
[FieldDefinition(7, Type = FieldType.Checkbox, Label = "Search in filenames", HelpText = "Search text in the filenames")]
|
||||
public bool SearchInFilenames { get; set; }
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
|
||||
public enum MyAnonamouseSearchType
|
||||
{
|
||||
[FieldOption(Label="All torrents", Hint = "Search everything")]
|
||||
All = 0,
|
||||
[FieldOption(Label="Only active", Hint = "Last update had 1+ seeders")]
|
||||
Active = 1,
|
||||
[FieldOption(Label="Freeleech", Hint = "Freeleech torrents")]
|
||||
Freeleech = 2,
|
||||
[FieldOption(Label="Freeleech or VIP", Hint = "Freeleech or VIP torrents")]
|
||||
FreeleechOrVip = 3,
|
||||
[FieldOption(Label="VIP", Hint = "VIP torrents")]
|
||||
Vip = 4,
|
||||
[FieldOption(Label="Not VIP", Hint = "Torrents not VIP")]
|
||||
NotVip = 5,
|
||||
}
|
||||
|
||||
public class MyAnonamouseTorrent
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Title { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "author_info")]
|
||||
public string AuthorInfo { get; set; }
|
||||
|
||||
public string Description { get; set; }
|
||||
[JsonProperty(PropertyName = "lang_code")]
|
||||
public string LangCode { get; set; }
|
||||
public string LanguageCode { get; set; }
|
||||
public string Filetype { get; set; }
|
||||
public bool Vip { get; set; }
|
||||
public bool Free { get; set; }
|
||||
[JsonProperty(PropertyName = "fl_vip")]
|
||||
public bool FreeVip { get; set; }
|
||||
public string Category { get; set; }
|
||||
public string Added { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "times_completed")]
|
||||
public int Grabs { get; set; }
|
||||
public int Seeders { get; set; }
|
||||
@@ -489,9 +571,15 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public List<MyAnonamouseTorrent> Data { get; set; }
|
||||
}
|
||||
|
||||
public class MyAnonamouseFreeleechResponse
|
||||
public class MyAnonamouseBuyPersonalFreeleechResponse
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Error { get; set; }
|
||||
}
|
||||
|
||||
public class MyAnonamouseUserDataResponse
|
||||
{
|
||||
[JsonProperty(PropertyName = "class")]
|
||||
public string UserClass { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FluentValidation;
|
||||
using NLog;
|
||||
@@ -25,7 +24,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public class Orpheus : TorrentIndexerBase<OrpheusSettings>
|
||||
{
|
||||
public override string Name => "Orpheus";
|
||||
public override string[] IndexerUrls => new string[] { "https://orpheus.network/" };
|
||||
public override string[] IndexerUrls => new[] { "https://orpheus.network/" };
|
||||
public override string Description => "Orpheus (APOLLO) is a Private Torrent Tracker for MUSIC";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
@@ -39,7 +38,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new OrpheusRequestGenerator() { Settings = Settings, Capabilities = Capabilities, HttpClient = _httpClient };
|
||||
return new OrpheusRequestGenerator { Settings = Settings, Capabilities = Capabilities, HttpClient = _httpClient };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
@@ -52,13 +51,13 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q, MusicSearchParam.Album, MusicSearchParam.Artist, MusicSearchParam.Label, MusicSearchParam.Year
|
||||
},
|
||||
{
|
||||
MusicSearchParam.Q, MusicSearchParam.Album, MusicSearchParam.Artist, MusicSearchParam.Label, MusicSearchParam.Year
|
||||
},
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Audio, "Music");
|
||||
@@ -121,10 +120,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
public IIndexerHttpClient HttpClient { get; set; }
|
||||
|
||||
public OrpheusRequestGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
@@ -260,24 +255,14 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
foreach (var torrent in result.Torrents)
|
||||
{
|
||||
var id = torrent.TorrentId;
|
||||
var artist = WebUtility.HtmlDecode(result.Artist);
|
||||
var album = WebUtility.HtmlDecode(result.GroupName);
|
||||
|
||||
var title = $"{result.Artist} - {result.GroupName} ({result.GroupYear}) [{torrent.Format} {torrent.Encoding}] [{torrent.Media}]";
|
||||
if (torrent.HasCue)
|
||||
{
|
||||
title += " [Cue]";
|
||||
}
|
||||
|
||||
var title = GetTitle(result, torrent);
|
||||
var infoUrl = GetInfoUrl(result.GroupId, id);
|
||||
|
||||
GazelleInfo release = new GazelleInfo()
|
||||
var release = new GazelleInfo
|
||||
{
|
||||
Guid = infoUrl,
|
||||
|
||||
// Splice Title from info to avoid calling API again for every torrent.
|
||||
Title = WebUtility.HtmlDecode(title),
|
||||
|
||||
Container = torrent.Encoding,
|
||||
Codec = torrent.Format,
|
||||
Size = long.Parse(torrent.Size),
|
||||
@@ -314,7 +299,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var id = result.TorrentId;
|
||||
var infoUrl = GetInfoUrl(result.GroupId, id);
|
||||
|
||||
GazelleInfo release = new GazelleInfo()
|
||||
var release = new GazelleInfo
|
||||
{
|
||||
Guid = infoUrl,
|
||||
Title = WebUtility.HtmlDecode(result.GroupName),
|
||||
@@ -352,6 +337,25 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private string GetTitle(GazelleRelease result, GazelleTorrent torrent)
|
||||
{
|
||||
var title = $"{result.Artist} - {result.GroupName} ({result.GroupYear})";
|
||||
|
||||
if (torrent.RemasterTitle.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
title += $" [{string.Format("{0} {1}", torrent.RemasterTitle, torrent.RemasterYear).Trim()}]";
|
||||
}
|
||||
|
||||
title += $" [{torrent.Format} {torrent.Encoding}] [{torrent.Media}]";
|
||||
|
||||
if (torrent.HasCue)
|
||||
{
|
||||
title += " [Cue]";
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
private string GetDownloadUrl(int torrentId, bool canUseToken)
|
||||
{
|
||||
// AuthKey is required but not checked, just pass in a dummy variable
|
||||
@@ -391,7 +395,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public class OrpheusSettings : NoAuthTorrentBaseSettings
|
||||
{
|
||||
private static readonly OrpheusSettingsValidator Validator = new OrpheusSettingsValidator();
|
||||
private static readonly OrpheusSettingsValidator Validator = new ();
|
||||
|
||||
public OrpheusSettings()
|
||||
{
|
||||
|
||||
@@ -6,21 +6,26 @@ using NzbDrone.Core.Messaging.Events;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
public class PrivateHD : Avistaz.AvistazBase
|
||||
public class PrivateHD : AvistazBase
|
||||
{
|
||||
public override string Name => "PrivateHD";
|
||||
public override string[] IndexerUrls => new string[] { "https://privatehd.to/" };
|
||||
public override string[] IndexerUrls => new[] { "https://privatehd.to/" };
|
||||
public override string Description => "PrivateHD is a Private Torrent Tracker for HD MOVIES / TV and the sister-site of AvistaZ, CinemaZ, ExoticaZ, and AnimeTorrents";
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
|
||||
public PrivateHD(IIndexerRepository indexerRepository, IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
public PrivateHD(IIndexerRepository indexerRepository,
|
||||
IIndexerHttpClient httpClient,
|
||||
IEventAggregator eventAggregator,
|
||||
IIndexerStatusService indexerStatusService,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
: base(indexerRepository, httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new AvistazRequestGenerator()
|
||||
return new AvistazRequestGenerator
|
||||
{
|
||||
Settings = Settings,
|
||||
HttpClient = _httpClient,
|
||||
@@ -34,13 +39,13 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId, TvSearchParam.TvdbId, TvSearchParam.Genre
|
||||
},
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId, TvSearchParam.TvdbId, TvSearchParam.Genre
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId, MovieSearchParam.TmdbId, MovieSearchParam.Genre
|
||||
}
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId, MovieSearchParam.TmdbId, MovieSearchParam.Genre
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Movies);
|
||||
|
||||
@@ -17,19 +17,15 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
||||
{
|
||||
public class Rarbg : TorrentIndexerBase<RarbgSettings>
|
||||
{
|
||||
private readonly IRarbgTokenProvider _tokenProvider;
|
||||
|
||||
public override string Name => "Rarbg";
|
||||
public override string[] IndexerUrls => new string[] { "https://torrentapi.org" };
|
||||
public override string[] IndexerUrls => new[] { "https://torrentapi.org/" };
|
||||
public override string[] LegacyUrls => new[] { "https://torrentapi.org" };
|
||||
public override string Description => "RARBG is a Public torrent site for MOVIES / TV / GENERAL";
|
||||
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Public;
|
||||
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
public override TimeSpan RateLimit => TimeSpan.FromSeconds(4);
|
||||
public override TimeSpan RateLimit => TimeSpan.FromSeconds(5);
|
||||
private readonly IRarbgTokenProvider _tokenProvider;
|
||||
|
||||
public Rarbg(IRarbgTokenProvider tokenProvider, IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
@@ -44,7 +40,7 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new RarbgParser(Capabilities);
|
||||
return new RarbgParser(Capabilities, _logger);
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
@@ -52,17 +48,17 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId, TvSearchParam.TvdbId
|
||||
},
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId, TvSearchParam.TvdbId
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId, MovieSearchParam.TmdbId
|
||||
},
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId, MovieSearchParam.TmdbId
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
}
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.XXX, "XXX (18+)");
|
||||
@@ -106,7 +102,7 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
||||
|
||||
if (jsonResponse.Resource.error_code.HasValue)
|
||||
{
|
||||
if (jsonResponse.Resource.error_code == 4 || jsonResponse.Resource.error_code == 2)
|
||||
if (jsonResponse.Resource.error_code is 4 or 2)
|
||||
{
|
||||
_logger.Debug("Invalid or expired token, refreshing token from Rarbg");
|
||||
_tokenProvider.ExpireToken(Settings);
|
||||
@@ -118,7 +114,7 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
||||
request.HttpRequest.Url = request.Url.SetQuery(qs.GetQueryString());
|
||||
response = await FetchIndexerResponse(request);
|
||||
}
|
||||
else if (jsonResponse.Resource.error_code == 5)
|
||||
else if (jsonResponse.Resource.error_code is 5)
|
||||
{
|
||||
_logger.Debug("Rarbg temp rate limit hit, retrying request");
|
||||
response = await FetchIndexerResponse(request);
|
||||
@@ -155,9 +151,9 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
||||
Settings.Validate().Filter("BaseUrl").ThrowOnError();
|
||||
|
||||
var request = new HttpRequestBuilder(Settings.BaseUrl.Trim('/'))
|
||||
.Resource($"/pubapi_v2.php?get_token=get_token&app_id={BuildInfo.AppName}")
|
||||
.Accept(HttpAccept.Json)
|
||||
.Build();
|
||||
.Resource($"/pubapi_v2.php?get_token=get_token&app_id={BuildInfo.AppName}")
|
||||
.Accept(HttpAccept.Json)
|
||||
.Build();
|
||||
|
||||
_httpClient.Get(request);
|
||||
|
||||
@@ -166,7 +162,8 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
||||
captchaToken = ""
|
||||
};
|
||||
}
|
||||
else if (action == "getCaptchaCookie")
|
||||
|
||||
if (action == "getCaptchaCookie")
|
||||
{
|
||||
if (query["responseUrl"].IsNullOrWhiteSpace())
|
||||
{
|
||||
@@ -200,7 +197,8 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
||||
captchaToken = cfClearanceCookie
|
||||
};
|
||||
}
|
||||
else if (action == "getUrls")
|
||||
|
||||
if (action == "getUrls")
|
||||
{
|
||||
var links = IndexerUrls;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
@@ -14,10 +15,12 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
||||
private static readonly Regex RegexGuid = new Regex(@"^magnet:\?xt=urn:btih:([a-f0-9]+)", RegexOptions.Compiled);
|
||||
|
||||
private readonly IndexerCapabilities _capabilities;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public RarbgParser(IndexerCapabilities capabilities)
|
||||
public RarbgParser(IndexerCapabilities capabilities, Logger logger)
|
||||
{
|
||||
_capabilities = capabilities;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
@@ -41,12 +44,19 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
||||
|
||||
if (jsonResponse.Resource.error_code.HasValue)
|
||||
{
|
||||
if (jsonResponse.Resource.error_code == 20 || jsonResponse.Resource.error_code == 8
|
||||
|| jsonResponse.Resource.error_code == 9 || jsonResponse.Resource.error_code == 10
|
||||
|| jsonResponse.Resource.error_code == 5 || jsonResponse.Resource.error_code == 13
|
||||
|| jsonResponse.Resource.error_code == 14)
|
||||
if (jsonResponse.Resource.error_code is 20 or 8 or 9 or 10 or 5 or 13 or 14)
|
||||
{
|
||||
// No results, rate limit, or imdbid/tvdb not found
|
||||
var reason = $"{jsonResponse.Resource.error} ({jsonResponse.Resource.error_code})";
|
||||
|
||||
if (jsonResponse.Resource.rate_limit is 1)
|
||||
{
|
||||
_logger.Debug("No results due to rate limiting. Reason: {0}", reason);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Debug("No results or imdbid/tvdb not found. Reason: {0}", reason);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,12 +22,11 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
||||
|
||||
private IEnumerable<IndexerRequest> GetRequest(string term, int[] categories, string imdbId = null, int? tmdbId = null, int? tvdbId = null)
|
||||
{
|
||||
var requestBuilder = new HttpRequestBuilder(Settings.BaseUrl)
|
||||
var requestBuilder = new HttpRequestBuilder(Settings.BaseUrl.Trim('/'))
|
||||
.Resource("/pubapi_v2.php")
|
||||
.AddQueryParam("mode", "search")
|
||||
.Accept(HttpAccept.Json);
|
||||
|
||||
requestBuilder.AddQueryParam("mode", "search");
|
||||
|
||||
if (imdbId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
requestBuilder.AddQueryParam("search_imdb", imdbId);
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
||||
() =>
|
||||
{
|
||||
var requestBuilder = new HttpRequestBuilder(settings.BaseUrl.Trim('/'))
|
||||
.WithRateLimit(3.0)
|
||||
.WithRateLimit(5.0)
|
||||
.Resource($"/pubapi_v2.php?get_token=get_token&app_id={BuildInfo.AppName}")
|
||||
.Accept(HttpAccept.Json);
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using AngleSharp.Html.Parser;
|
||||
using FluentValidation;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
@@ -16,7 +15,6 @@ using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
@@ -38,7 +36,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new SceneTimeRequestGenerator() { Settings = Settings, Capabilities = Capabilities };
|
||||
return new SceneTimeRequestGenerator { Settings = Settings, Capabilities = Capabilities };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
@@ -56,21 +54,21 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q
|
||||
},
|
||||
{
|
||||
MovieSearchParam.Q
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.XXX, "Movies Adult");
|
||||
@@ -112,10 +110,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public SceneTimeSettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
|
||||
public SceneTimeRequestGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories)
|
||||
{
|
||||
var qc = new NameValueCollection
|
||||
@@ -280,11 +274,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public class SceneTimeSettings : CookieTorrentBaseSettings
|
||||
{
|
||||
public SceneTimeSettings()
|
||||
{
|
||||
}
|
||||
|
||||
[FieldDefinition(3, Label = "FreeLeech Only", Type = FieldType.Checkbox, Advanced = true, HelpText = "Search Freeleech torrents only")]
|
||||
[FieldDefinition(3, Label = "FreeLeech Only", Type = FieldType.Checkbox, HelpText = "Search Freeleech torrents only")]
|
||||
public bool FreeLeechOnly { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
[Obsolete("Site unavailable")]
|
||||
public class Shizaproject : TorrentIndexerBase<NoAuthTorrentBaseSettings>
|
||||
{
|
||||
public override string Name => "ShizaProject";
|
||||
|
||||
@@ -7,7 +7,6 @@ using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Html.Parser;
|
||||
using FluentValidation;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
@@ -19,14 +18,13 @@ using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
public class SpeedCD : TorrentIndexerBase<UserPassTorrentBaseSettings>
|
||||
public class SpeedCD : TorrentIndexerBase<SpeedCDSettings>
|
||||
{
|
||||
public override string Name => "SpeedCD";
|
||||
public override string[] IndexerUrls => new string[]
|
||||
public override string[] IndexerUrls => new[]
|
||||
{
|
||||
"https://speed.cd/",
|
||||
"https://speed.click/",
|
||||
@@ -40,14 +38,18 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
public SpeedCD(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
public SpeedCD(IIndexerHttpClient httpClient,
|
||||
IEventAggregator eventAggregator,
|
||||
IIndexerStatusService indexerStatusService,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new SpeedCDRequestGenerator() { Settings = Settings, Capabilities = Capabilities, Encoding = Encoding };
|
||||
return new SpeedCDRequestGenerator { Settings = Settings, Capabilities = Capabilities, Encoding = Encoding };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
@@ -113,12 +115,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
|
||||
{
|
||||
if (!httpResponse.Content.Contains("/browse.php"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return !httpResponse.Content.Contains("/browse.php");
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
@@ -126,21 +123,21 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
|
||||
},
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.MoviesOther, "Movies/XviD");
|
||||
@@ -182,17 +179,13 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public class SpeedCDRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
public UserPassTorrentBaseSettings Settings { get; set; }
|
||||
public SpeedCDSettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
public Encoding Encoding { get; set; }
|
||||
|
||||
public SpeedCDRequestGenerator()
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories, bool deep = false)
|
||||
{
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories, string imdbId = null)
|
||||
{
|
||||
var searchUrl = string.Format("{0}/browse/", Settings.BaseUrl.TrimEnd('/'));
|
||||
var searchUrl = $"{Settings.BaseUrl.TrimEnd('/')}/browse/";
|
||||
|
||||
var qc = new List<string>();
|
||||
|
||||
@@ -202,18 +195,19 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
qc.Add(cat);
|
||||
}
|
||||
|
||||
if (imdbId.IsNotNullOrWhiteSpace())
|
||||
if (Settings.FreeleechOnly)
|
||||
{
|
||||
qc.Add("freeleech");
|
||||
}
|
||||
|
||||
if (deep)
|
||||
{
|
||||
qc.Add("deep");
|
||||
qc.Add("q");
|
||||
qc.Add(imdbId);
|
||||
}
|
||||
else
|
||||
{
|
||||
qc.Add("q");
|
||||
qc.Add(term.UrlEncode(Encoding));
|
||||
}
|
||||
|
||||
qc.Add("q");
|
||||
qc.Add(term.UrlEncode(Encoding));
|
||||
|
||||
searchUrl += string.Join("/", qc);
|
||||
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Html);
|
||||
@@ -225,7 +219,14 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
var term = $"{searchCriteria.SanitizedSearchTerm}";
|
||||
|
||||
if (searchCriteria.FullImdbId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
term = $"{searchCriteria.FullImdbId}";
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(term.Trim(), searchCriteria.Categories, searchCriteria.FullImdbId.IsNotNullOrWhiteSpace()));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -234,7 +235,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -243,7 +244,24 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
var term = $"{searchCriteria.SanitizedTvSearchString}";
|
||||
|
||||
if (searchCriteria.FullImdbId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
term = $"{searchCriteria.FullImdbId}";
|
||||
|
||||
if (searchCriteria.EpisodeSearchString.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
term += $" {searchCriteria.EpisodeSearchString}";
|
||||
}
|
||||
|
||||
if (searchCriteria.Season.HasValue)
|
||||
{
|
||||
term += "*";
|
||||
}
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(term.Trim(), searchCriteria.Categories, searchCriteria.FullImdbId.IsNotNullOrWhiteSpace()));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -252,7 +270,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -261,7 +279,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -272,10 +290,10 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public class SpeedCDParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly UserPassTorrentBaseSettings _settings;
|
||||
private readonly SpeedCDSettings _settings;
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
|
||||
public SpeedCDParser(UserPassTorrentBaseSettings settings, IndexerCapabilitiesCategories categories)
|
||||
public SpeedCDParser(SpeedCDSettings settings, IndexerCapabilitiesCategories categories)
|
||||
{
|
||||
_settings = settings;
|
||||
_categories = categories;
|
||||
@@ -291,28 +309,26 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var cells = row.QuerySelectorAll("td");
|
||||
var title = Regex.Replace(row.QuerySelector("td:nth-child(2) > div > a[href^=\"/t/\"]").TextContent, @"(?i:\[REQ\])", "").Trim(' ', '.');
|
||||
var downloadUrl = new Uri(_settings.BaseUrl + row.QuerySelector("td:nth-child(4) a[href^=\"/download/\"]").GetAttribute("href").TrimStart('/'));
|
||||
var infoUrl = new Uri(_settings.BaseUrl + row.QuerySelector("td:nth-child(2) > div > a[href^=\"/t/\"]").GetAttribute("href").TrimStart('/'));
|
||||
var size = ParseUtil.GetBytes(row.QuerySelector("td:nth-child(6)").TextContent);
|
||||
var grabs = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(7)").TextContent);
|
||||
var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(8)").TextContent);
|
||||
var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(9)").TextContent);
|
||||
|
||||
var title = row.QuerySelector("td[class='lft'] > div > a").TextContent.Trim();
|
||||
var link = new Uri(_settings.BaseUrl + row.QuerySelector("img[title='Download']").ParentElement.GetAttribute("href").TrimStart('/'));
|
||||
var details = new Uri(_settings.BaseUrl + row.QuerySelector("td[class='lft'] > div > a").GetAttribute("href").TrimStart('/'));
|
||||
var size = ParseUtil.GetBytes(cells[5].TextContent);
|
||||
var grabs = ParseUtil.CoerceInt(cells[6].TextContent);
|
||||
var seeders = ParseUtil.CoerceInt(cells[7].TextContent);
|
||||
var leechers = ParseUtil.CoerceInt(cells[8].TextContent);
|
||||
|
||||
var pubDateStr = row.QuerySelector("span[class^='elapsedDate']").GetAttribute("title").Replace(" at", "");
|
||||
var pubDateStr = row.QuerySelector("td:nth-child(2) span[class^=\"elapsedDate\"]").GetAttribute("title").Replace(" at", "");
|
||||
var publishDate = DateTime.ParseExact(pubDateStr, "dddd, MMMM d, yyyy h:mmtt", CultureInfo.InvariantCulture);
|
||||
|
||||
var cat = row.QuerySelector("a").GetAttribute("href").Split('/').Last();
|
||||
var downloadVolumeFactor = row.QuerySelector("span:contains(\"[Freeleech]\")") != null ? 0 : 1;
|
||||
var cat = row.QuerySelector("td:nth-child(1) a").GetAttribute("href").Split('/').Last();
|
||||
var downloadVolumeFactor = row.QuerySelector("td:nth-child(2) span:contains(\"[Freeleech]\")") != null ? 0 : 1;
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Title = title,
|
||||
DownloadUrl = link.AbsoluteUri,
|
||||
Guid = link.AbsoluteUri,
|
||||
InfoUrl = details.AbsoluteUri,
|
||||
DownloadUrl = downloadUrl.AbsoluteUri,
|
||||
Guid = infoUrl.AbsoluteUri,
|
||||
InfoUrl = infoUrl.AbsoluteUri,
|
||||
PublishDate = publishDate,
|
||||
Categories = _categories.MapTrackerCatToNewznab(cat),
|
||||
Size = size,
|
||||
@@ -333,4 +349,10 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
public class SpeedCDSettings : UserPassTorrentBaseSettings
|
||||
{
|
||||
[FieldDefinition(4, Label = "Freeleech Only", Type = FieldType.Checkbox, HelpText = "Search freeleech torrents only")]
|
||||
public bool FreeleechOnly { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,5 +466,6 @@
|
||||
"AreYouSureYouWantToDeleteCategory": "Haluatko varmasti poistaa kartoitetun kategorian?",
|
||||
"DeleteClientCategory": "Poista lataustyökalukategoria",
|
||||
"DownloadClientCategory": "Lataustyökalukategoria",
|
||||
"MappedCategories": "Kartoitetut kategoriat"
|
||||
"MappedCategories": "Kartoitetut kategoriat",
|
||||
"AuthenticationRequired": "Todennus vaaditaan"
|
||||
}
|
||||
|
||||
@@ -469,5 +469,5 @@
|
||||
"DownloadClientCategory": "Letöltési kliens kategória",
|
||||
"AuthenticationRequired": "Azonosítás szükséges",
|
||||
"AuthenticationRequiredHelpText": "Módosítsd, hogy mely kérésekhez van szükség hitelesítésre. Ne változtasson, hacsak nem érti a kockázatokat.",
|
||||
"AuthenticationRequiredWarning": "A hitelesítés nélküli távoli hozzáférés megakadályozása érdekében a Prowlarrnak mostantól engedélyezni kell a hitelesítést. Opcionálisan letilthatja a helyi címekről történő hitelesítést."
|
||||
"AuthenticationRequiredWarning": "A hitelesítés nélküli távoli hozzáférés megakadályozása érdekében a Prowlarrnak mostantól engedélyezni kell a hitelesítést. Konfigurálja a hitelesítési módszert és a hitelesítési adatokat. Opcionálisan letilthatja a helyi címekről történő hitelesítést. További információkért tekintsd meg a GYIK-et."
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@
|
||||
"CertificateValidation": "Certificaat Validatie",
|
||||
"BypassProxyForLocalAddresses": "Omzeil Proxy voor Lokale Adressen",
|
||||
"Branch": "Branch",
|
||||
"BindAddressHelpText": "Geldig IPv4 adres of '*' voor alle interfaces",
|
||||
"BindAddressHelpText": "Geldig IP-adres, localhost of '*' voor alle interfaces",
|
||||
"DeleteBackup": "Verwijder Veiligheidskopie",
|
||||
"BackupIntervalHelpText": "Tussentijd voor automatische back-up",
|
||||
"Backups": "Veiligheidskopieën",
|
||||
|
||||
@@ -469,5 +469,5 @@
|
||||
"MappedCategories": "Categorias Mapeadas",
|
||||
"AuthenticationRequired": "Autenticação Requerida",
|
||||
"AuthenticationRequiredHelpText": "Altera para quais solicitações a autenticação é necessária. Não mude a menos que você entenda os riscos.",
|
||||
"AuthenticationRequiredWarning": "Para impedir o acesso remoto sem autenticação, o Prowlarr agora exige que a autenticação seja habilitada. Você pode, opcionalmente, desabilitar a autenticação de endereços locais."
|
||||
"AuthenticationRequiredWarning": "Para impedir o acesso remoto sem autenticação, o Prowlarr agora exige que a autenticação seja habilitada. Configure seu método de autenticação e credenciais. Você pode, opcionalmente, desabilitar a autenticação de endereços locais. Consulte o FAQ para obter informações adicionais."
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
"BackupFolderHelpText": "Относительные пути будут в каталоге AppData Radarr",
|
||||
"BeforeUpdate": "До обновления",
|
||||
"BindAddress": "Привязать адрес",
|
||||
"BindAddressHelpText": "Действительный IP4-адрес или '*' для всех интерфейсов",
|
||||
"BindAddressHelpText": "Действительный IP-адрес, локальный адрес или '*' для всех интерфейсов",
|
||||
"Branch": "Ветка",
|
||||
"BranchUpdate": "Ветвь для обновления Radarr",
|
||||
"BranchUpdateMechanism": "Ветвь, используемая внешним механизмом обновления",
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
"ApplyTagsHelpTexts2": "Додати: додати теги до наявного списку тегів",
|
||||
"AuthenticationMethodHelpText": "Для доступу до Radarr потрібні ім’я користувача та пароль",
|
||||
"BackupFolderHelpText": "Відносні шляхи будуть у каталозі AppData Radarr",
|
||||
"BindAddressHelpText": "Дійсна адреса IPv4 або '*' для всіх інтерфейсів",
|
||||
"BindAddressHelpText": "Дійсна адреса IP або '*' для всіх інтерфейсів",
|
||||
"BranchUpdate": "Гілка для оновлення Radarr",
|
||||
"AllIndexersHiddenDueToFilter": "Всі фільми заховані відповідно до фільтра.",
|
||||
"AnalyticsEnabledHelpText": "Надсилайте анонімну інформацію про використання та помилки на сервери Radarr. Це включає інформацію про ваш веб-переглядач, які сторінки Radarr WebUI ви використовуєте, звіти про помилки, а також версію ОС і часу виконання. Ми будемо використовувати цю інформацію, щоб визначити пріоритети функцій і виправлення помилок.",
|
||||
@@ -212,7 +212,7 @@
|
||||
"Exception": "Виняток",
|
||||
"ExistingTag": "Існуючий тег",
|
||||
"Failed": "Не вдалося",
|
||||
"FeatureRequests": "Запити щодо функцій",
|
||||
"FeatureRequests": "Майбутні запити",
|
||||
"Events": "Події",
|
||||
"Fixed": "Виправлено",
|
||||
"Filters": "Фільтри",
|
||||
@@ -293,7 +293,7 @@
|
||||
"Enable": "Увімкнути",
|
||||
"Filename": "Ім'я файлу",
|
||||
"Host": "Хост",
|
||||
"PriorityHelpText": "Надайте пріоритет кільком клієнтам завантаження. Round-Robin використовується для клієнтів з однаковим пріоритетом.",
|
||||
"PriorityHelpText": "Надайте пріоритет кільком клієнтам завантаження. Круговий алгоритм використовується для клієнтів з таким же пріоритетом.",
|
||||
"SSLCertPathHelpText": "Шлях до файлу pfx",
|
||||
"SSLPort": "Порт SSL",
|
||||
"Started": "Розпочато",
|
||||
@@ -303,5 +303,19 @@
|
||||
"Style": "Стиль",
|
||||
"SuggestTranslationChange": "Запропонуйте зміну перекладу",
|
||||
"TableOptionsColumnsMessage": "Виберіть, які стовпці відображаються та в якому порядку вони відображаються",
|
||||
"SystemTimeCheckMessage": "Системний час вимкнено більш ніж на 1 день. Заплановані завдання можуть не працювати належним чином, доки час не буде виправлено"
|
||||
"SystemTimeCheckMessage": "Системний час вимкнено більш ніж на 1 день. Заплановані завдання можуть не працювати належним чином, доки час не буде виправлено",
|
||||
"OnGrab": "При захопленні",
|
||||
"SSLCertPath": "Шлях сертифіката SSL",
|
||||
"UI": "Інтерфейс користувача",
|
||||
"Reddit": "Reddit",
|
||||
"RSS": "RSS",
|
||||
"Seeders": "Сиди",
|
||||
"Wiki": "Wiki",
|
||||
"Grabbed": "Захоплений",
|
||||
"Logging": "Журналування",
|
||||
"NetCore": ".NET",
|
||||
"Peers": "Піри",
|
||||
"Usenet": "Usenet",
|
||||
"SSLCertPassword": "Пароль SSL сертифіката",
|
||||
"MaintenanceRelease": "Випуск для обслуговування: виправлення помилок та інші покращення. Щоб отримати докладнішу інформацію, перегляньте історію фіксації Github"
|
||||
}
|
||||
|
||||
53
src/NzbDrone.Core/Notifications/Mailgun/Mailgun.cs
Normal file
53
src/NzbDrone.Core/Notifications/Mailgun/Mailgun.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Mailgun
|
||||
{
|
||||
public class MailGun : NotificationBase<MailgunSettings>
|
||||
{
|
||||
private readonly IMailgunProxy _proxy;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public MailGun(IMailgunProxy proxy, Logger logger)
|
||||
{
|
||||
_proxy = proxy;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override string Name => "Mailgun";
|
||||
public override string Link => "https://mailgun.com";
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheckMessage)
|
||||
{
|
||||
_proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheckMessage.Message, Settings);
|
||||
}
|
||||
|
||||
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
|
||||
{
|
||||
_proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings);
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
||||
try
|
||||
{
|
||||
const string title = "Test Notification";
|
||||
const string body = "This is a test message from Prowlarr, though Mailgun.";
|
||||
|
||||
_proxy.SendNotification(title, body, Settings);
|
||||
_logger.Info("Successsfully sent email though Mailgun.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message though Mailgun.");
|
||||
failures.Add(new ValidationFailure("", "Unable to send test message though Mailgun."));
|
||||
}
|
||||
|
||||
return new ValidationResult(failures);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/NzbDrone.Core/Notifications/Mailgun/MailgunException.cs
Normal file
18
src/NzbDrone.Core/Notifications/Mailgun/MailgunException.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using NzbDrone.Common.Exceptions;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Mailgun
|
||||
{
|
||||
public class MailgunException : NzbDroneException
|
||||
{
|
||||
public MailgunException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public MailgunException(string message, Exception innerException, params object[] args)
|
||||
: base(message, innerException, args)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
68
src/NzbDrone.Core/Notifications/Mailgun/MailgunProxy.cs
Normal file
68
src/NzbDrone.Core/Notifications/Mailgun/MailgunProxy.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Mailgun
|
||||
{
|
||||
public interface IMailgunProxy
|
||||
{
|
||||
void SendNotification(string title, string message, MailgunSettings settings);
|
||||
}
|
||||
|
||||
public class MailgunProxy : IMailgunProxy
|
||||
{
|
||||
private const string BaseUrlEu = "https://api.eu.mailgun.net/v3";
|
||||
private const string BaseUrlUs = "https://api.mailgun.net/v3";
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public MailgunProxy(IHttpClient httpClient, Logger logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void SendNotification(string title, string message, MailgunSettings settings)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = BuildRequest(settings, $"{settings.SenderDomain}/messages", HttpMethod.Post, title, message).Build();
|
||||
_httpClient.Execute(request);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
_logger.Error("Unathorized - ApiKey is invalid");
|
||||
throw new MailgunException("Unauthorized - ApiKey is invalid");
|
||||
}
|
||||
|
||||
_logger.Error(ex, "Unable to connect to Mailgun. Status code: " + ex.Message);
|
||||
throw new MailgunException("Unable to connect to Mailgun. Status code: {0}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private HttpRequestBuilder BuildRequest(MailgunSettings settings, string resource, HttpMethod method, string messageSubject, string messageBody)
|
||||
{
|
||||
var loginCredentials = new NetworkCredential("api", settings.ApiKey);
|
||||
var url = settings.UseEuEndpoint ? BaseUrlEu : BaseUrlUs;
|
||||
var requestBuilder = new HttpRequestBuilder(url).Resource(resource);
|
||||
|
||||
requestBuilder.Method = method;
|
||||
requestBuilder.NetworkCredential = loginCredentials;
|
||||
|
||||
requestBuilder.AddFormParameter("from", $"{settings.From}");
|
||||
|
||||
foreach (var recipient in settings.Recipients)
|
||||
{
|
||||
requestBuilder.AddFormParameter("to", $"{recipient}");
|
||||
}
|
||||
|
||||
requestBuilder.AddFormParameter("subject", $"{messageSubject}");
|
||||
requestBuilder.AddFormParameter("text", $"{messageBody}");
|
||||
|
||||
return requestBuilder;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/NzbDrone.Core/Notifications/Mailgun/MailgunSettings.cs
Normal file
49
src/NzbDrone.Core/Notifications/Mailgun/MailgunSettings.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Mailgun
|
||||
{
|
||||
public class MailGunSettingsValidator : AbstractValidator<MailgunSettings>
|
||||
{
|
||||
public MailGunSettingsValidator()
|
||||
{
|
||||
RuleFor(c => c.ApiKey).NotEmpty();
|
||||
RuleFor(c => c.From).NotEmpty();
|
||||
RuleFor(c => c.Recipients).NotEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public class MailgunSettings : IProviderConfig
|
||||
{
|
||||
private static readonly MailGunSettingsValidator Validator = new MailGunSettingsValidator();
|
||||
|
||||
public MailgunSettings()
|
||||
{
|
||||
Recipients = Array.Empty<string>();
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "API Key", HelpText = "The API key generated from MailGun")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Use EU Endpoint?", HelpText = "Use the EU MailGun endpoint", Type = FieldType.Checkbox)]
|
||||
public bool UseEuEndpoint { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "From Address")]
|
||||
public string From { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Sender Domain")]
|
||||
public string SenderDomain { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Recipient Address(es)", Type = FieldType.Tag, Placeholder = "example@email.com,example1@email.com")]
|
||||
public IEnumerable<string> Recipients { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
22
yarn.lock
22
yarn.lock
@@ -1577,6 +1577,11 @@ acorn-jsx@^5.3.2:
|
||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
||||
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||
|
||||
acorn@^6.0.6:
|
||||
version "6.4.2"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
|
||||
integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==
|
||||
|
||||
acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0:
|
||||
version "8.8.1"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73"
|
||||
@@ -1726,6 +1731,16 @@ archiver@^5.3.1:
|
||||
tar-stream "^2.2.0"
|
||||
zip-stream "^4.1.0"
|
||||
|
||||
are-you-es5@2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/are-you-es5/-/are-you-es5-2.1.2.tgz#d75511a174a3f842d70cc784aec0bcaff5a57547"
|
||||
integrity sha512-gkT2bLCfzyJJr3lYoxZKScdD/Yp5zzghi+3KawONTvH/7rrgU3RMXYvGQnOTlqEFVgZFpEGj/6bZ6sF/9YtddA==
|
||||
dependencies:
|
||||
acorn "^6.0.6"
|
||||
array-flatten "^2.1.0"
|
||||
commander "^2.19.0"
|
||||
find-up "^4.1.0"
|
||||
|
||||
argparse@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
|
||||
@@ -1749,6 +1764,11 @@ arr-union@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-2.1.0.tgz#20f9eab5ec70f5c7d215b1077b1c39161d292c7d"
|
||||
integrity sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==
|
||||
|
||||
array-flatten@^2.1.0:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099"
|
||||
integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==
|
||||
|
||||
array-includes@^3.1.4, array-includes@^3.1.5, array-includes@^3.1.6:
|
||||
version "3.1.6"
|
||||
resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f"
|
||||
@@ -2197,7 +2217,7 @@ colorette@^2.0.14:
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798"
|
||||
integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==
|
||||
|
||||
commander@^2.20.0, commander@^2.20.3:
|
||||
commander@^2.19.0, commander@^2.20.0, commander@^2.20.3:
|
||||
version "2.20.3"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||
|
||||
Reference in New Issue
Block a user