Compare commits
125 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0abec9c1b6 | ||
![]() |
6867c4a234 | ||
![]() |
4d2759bcd2 | ||
![]() |
982c3df1ac | ||
![]() |
3dfe2faa6b | ||
![]() |
21cffe2d35 | ||
![]() |
fa9bbaa18c | ||
![]() |
769ceb016f | ||
![]() |
51cb8762b1 | ||
![]() |
f04a97f237 | ||
![]() |
2a020f691c | ||
![]() |
5abdd35e1f | ||
![]() |
5a04e66abe | ||
![]() |
0c02c22654 | ||
![]() |
cd68d06dc3 | ||
![]() |
465e607415 | ||
![]() |
cbf6e45f64 | ||
![]() |
c95d5fd9e2 | ||
![]() |
5991fd62c1 | ||
![]() |
5384f85b5a | ||
![]() |
c33315f59c | ||
![]() |
7eaff55955 | ||
![]() |
71414b805b | ||
![]() |
50a06f640f | ||
![]() |
6cab5fbba8 | ||
![]() |
66e3f8eea5 | ||
![]() |
a26e07210f | ||
![]() |
e612a826d0 | ||
![]() |
4d2240ec6e | ||
![]() |
50fb9ccb5f | ||
![]() |
28d3f22fef | ||
![]() |
8612387e39 | ||
![]() |
7d93361839 | ||
![]() |
9c7ce468ee | ||
![]() |
1ad0e93d01 | ||
![]() |
b53cbce24a | ||
![]() |
087635f22a | ||
![]() |
0263a5f869 | ||
![]() |
b910e42668 | ||
![]() |
33b3740fca | ||
![]() |
6d1d78cb92 | ||
![]() |
d4c9c6d82b | ||
![]() |
485d2705b5 | ||
![]() |
ecd2b33dfc | ||
![]() |
c12736716d | ||
![]() |
c0bdedbae1 | ||
![]() |
2abf4f0ee6 | ||
![]() |
1fdd165d2e | ||
![]() |
55818f9cb6 | ||
![]() |
a4c670df78 | ||
![]() |
c8d558c1eb | ||
![]() |
f56b51e9e3 | ||
![]() |
2e966b674c | ||
![]() |
aa1e31a110 | ||
![]() |
79418d3b1f | ||
![]() |
76f01273d3 | ||
![]() |
ef99687187 | ||
![]() |
5d03e6ec99 | ||
![]() |
3e9165ce4a | ||
![]() |
2a7a950d63 | ||
![]() |
d3d78e48cb | ||
![]() |
791471a716 | ||
![]() |
7f7f6680b2 | ||
![]() |
128591d355 | ||
![]() |
593c2f083e | ||
![]() |
7743267501 | ||
![]() |
268f5a8ae5 | ||
![]() |
121736358b | ||
![]() |
1b0ea00c88 | ||
![]() |
4be1c19c50 | ||
![]() |
aa55849e62 | ||
![]() |
1d2093fc32 | ||
![]() |
5a5d83e5c1 | ||
![]() |
155632e85d | ||
![]() |
cac185f747 | ||
![]() |
6d0d502bd3 | ||
![]() |
ccde6fb53b | ||
![]() |
2afce9f2d9 | ||
![]() |
2647457706 | ||
![]() |
ac5e69a3b2 | ||
![]() |
45fb2a27c3 | ||
![]() |
fb3d7ae81b | ||
![]() |
9b62e8af71 | ||
![]() |
3f292b5e47 | ||
![]() |
bd2abddb09 | ||
![]() |
5135748d1d | ||
![]() |
0fc3d224ab | ||
![]() |
ac07cc34cd | ||
![]() |
3730e05f20 | ||
![]() |
2644fd813e | ||
![]() |
ece16d1075 | ||
![]() |
3b13fa84a4 | ||
![]() |
cda5ea3207 | ||
![]() |
0746616b43 | ||
![]() |
28199ab4be | ||
![]() |
b29c578adb | ||
![]() |
b42f2a0972 | ||
![]() |
040deb2bfb | ||
![]() |
ef8b4e685e | ||
![]() |
364860199c | ||
![]() |
3f2a6fd3f7 | ||
![]() |
2671cf00e0 | ||
![]() |
6a6941d01c | ||
![]() |
5b3862bc3d | ||
![]() |
0452f5ad06 | ||
![]() |
264fc995b2 | ||
![]() |
acc75acb9c | ||
![]() |
a5b1332f95 | ||
![]() |
8871a631b1 | ||
![]() |
bfb58f53f5 | ||
![]() |
d14717c88e | ||
![]() |
d8ff110d8b | ||
![]() |
23737d3b19 | ||
![]() |
b11516760b | ||
![]() |
3a7f8ce268 | ||
![]() |
7a5b2e5c6a | ||
![]() |
ead129eda9 | ||
![]() |
b7cd0aeca8 | ||
![]() |
708b45b02f | ||
![]() |
75f4342499 | ||
![]() |
24ad51ad15 | ||
![]() |
ecc3dd26db | ||
![]() |
aecee29219 | ||
![]() |
bda73dc9c1 | ||
![]() |
63d2407e4f |
34
README.md
@@ -4,7 +4,7 @@ This project is a new fork and is recruiting development help. If you are able
|
||||
|
||||
Jackett works as a proxy server: it translates queries from apps (Sonarr, SickRage, CouchPotato, Mylar, etc) into tracker-site-specific http queries, parses the html response, then sends results back to the requesting software. This allows for getting recent uploads (like RSS) and performing searches. Jackett is a single repository of maintained indexer scraping & translation logic - removing the burden from other apps.
|
||||
|
||||
Developer note: The software implements the [Torznab](https://github.com/Sonarr/Sonarr/wiki/Implementing-a-Torznab-indexer) (with [nZEDb](https://github.com/nZEDb/nZEDb/blob/master/docs/newznab_api_specification.txt) category numbering) and [TorrentPotato](https://github.com/RuudBurger/CouchPotatoServer/wiki/Couchpotato-torrent-provider) APIs.
|
||||
Developer note: The software implements the [Torznab](https://github.com/Sonarr/Sonarr/wiki/Implementing-a-Torznab-indexer) (with [nZEDb](https://github.com/nZEDb/nZEDb/blob/dev/docs/newznab_api_specification.txt) category numbering) and [TorrentPotato](https://github.com/RuudBurger/CouchPotatoServer/wiki/Couchpotato-torrent-provider) APIs.
|
||||
|
||||
|
||||
|
||||
@@ -16,46 +16,66 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
#### Supported Private Trackers
|
||||
* Abnormal
|
||||
* AlphaRatio
|
||||
* Andraste
|
||||
* AnimeBytes
|
||||
* Avistaz
|
||||
* BakaBT
|
||||
* bB
|
||||
* Best Friends
|
||||
* BeyondHD
|
||||
* Bit-City Reloaded
|
||||
* BIT-HDTV
|
||||
* BitMeTV
|
||||
* BitSoup
|
||||
* BlueTigers
|
||||
* BTN
|
||||
* CinemaZ
|
||||
* DanishBits
|
||||
* Demonoid
|
||||
* EuTorrents
|
||||
* DigitalHive
|
||||
* FileList
|
||||
* Fnt
|
||||
* French-ADN
|
||||
* Freshon
|
||||
* FunFile
|
||||
* Fuzer
|
||||
* Ghost City
|
||||
* HD-Space
|
||||
* HD-Torrents
|
||||
* Hebits
|
||||
* New Real World
|
||||
* Hounddawgs
|
||||
* House-of-Torrents
|
||||
* ILoveTorrents
|
||||
* Immortalseed
|
||||
* IPTorrents
|
||||
* PassThePopcorn
|
||||
* PirateTheNet
|
||||
* MoreThanTV
|
||||
* MyAnonamouse
|
||||
* myAmity
|
||||
* NCore
|
||||
* NextGen
|
||||
* Pretome
|
||||
* PrivateHD
|
||||
* RevolutionTT
|
||||
* SceneAccess
|
||||
* SceneFZ
|
||||
* SceneTime
|
||||
* Shazbat
|
||||
* SpeedCD
|
||||
* TehConnection
|
||||
* Torrent Network
|
||||
* TorrentBytes
|
||||
* TorrentDay
|
||||
* TorrentHeaven
|
||||
* TorrentLeech
|
||||
* TorrentShack
|
||||
* Torrent-Syndikat
|
||||
* TransmitheNet
|
||||
* TV Chaos UK
|
||||
* World-In-HD
|
||||
* x264
|
||||
* XSpeeds
|
||||
* Xthor
|
||||
|
||||
#### Installation on Windows
|
||||
|
||||
@@ -69,7 +89,7 @@ Jackett can also be run from the command line using JackettConsole.exe if you wo
|
||||
* Debian/Ubunutu: apt-get install libcurl-dev
|
||||
* Redhat/Fedora: yum install libcurl-devel
|
||||
* For other distros see the [Curl docs](http://curl.haxx.se/dlwiz/?type=devel).
|
||||
3. Download and extract the latest ```.tar.gz``` release from the [releases page](https://github.com/Jackett/Jackett/releases) and run Jackett using mono with the command "mono JackettConsole.exe".
|
||||
3. Download and extract the latest `Jackett.Binaries.Mono.tar.gz` release from the [releases page](https://github.com/Jackett/Jackett/releases) and run Jackett using mono with the command `mono JackettConsole.exe`.
|
||||
|
||||
Detailed instructions for [Ubuntu 14.x](http://www.htpcguides.com/install-jackett-on-ubuntu-14-x-for-custom-torrents-in-sonarr/) and [Ubuntu 15.x](http://www.htpcguides.com/install-jackett-ubuntu-15-x-for-custom-torrents-in-sonarr/)
|
||||
|
||||
@@ -94,9 +114,9 @@ You can get additional logging with the switches "-t -l". Please post logs if y
|
||||
Please supply as much information about the problem you are experiencing as possible. Your issue has a much greater chance of being resolved if logs are supplied so that we can see what is going on. Creating an issue with '### isn't working' doesn't help anyone to fix the problem.
|
||||
|
||||
### Contributing
|
||||
All contributions are welcome just send a pull request. Jackett's framework allows our team (and any other volunteering dev) to implement new trackers in an hour or two. If you'd like support for a new tracker but are not a developer then feel free to leave a request on the [issues page](https://github.com/zone117x/Jackett/issues). It is recommended to use Visual studio 2015 when making code changes in this project. We currently only support private trackers.
|
||||
All contributions are welcome just send a pull request. Jackett's framework allows our team (and any other volunteering dev) to implement new trackers in an hour or two. If you'd like support for a new tracker but are not a developer then feel free to leave a request on the [issues page](https://github.com/Jackett/Jackett/issues). It is recommended to use Visual studio 2015 when making code changes in this project. We currently only support private trackers.
|
||||
|
||||
|
||||
### Screenshots
|
||||
|
||||

|
||||

|
||||
|
@@ -7,7 +7,7 @@
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
|
@@ -60,6 +60,9 @@ namespace Jackett.Console
|
||||
[Option('n', "IgnoreSslErrors", HelpText = "[true/false] Linux Libcurl - Ignores invalid SSL certificates")]
|
||||
public bool? IgnoreSslErrors { get; set; }
|
||||
|
||||
[Option('d', "DataFolder", HelpText = "Specify the location of the data folder (Must be admin on Windows) eg. --DataFolder=\"D:\\Your Data\\Jackett\\\"")]
|
||||
public string DataFolder { get; set; }
|
||||
|
||||
[ParserState]
|
||||
public IParserState LastParserState { get; set; }
|
||||
}
|
||||
|
@@ -94,12 +94,12 @@
|
||||
<HintPath>..\packages\Microsoft.Owin.StaticFiles.3.0.1\lib\net45\Microsoft.Owin.StaticFiles.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.3.7\lib\net45\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
|
||||
|
@@ -99,6 +99,13 @@ namespace JackettConsole
|
||||
Engine.Logger.Info("Curl will ignore SSL certificate errors.");
|
||||
}
|
||||
|
||||
// Choose Data Folder
|
||||
if (!string.IsNullOrWhiteSpace(options.DataFolder))
|
||||
{
|
||||
Startup.CustomDataFolder = options.DataFolder.Replace("\"", string.Empty).Replace("'", string.Empty).Replace(@"\\", @"\");
|
||||
Engine.Logger.Info("Jackett Data will be stored in: " + Startup.CustomDataFolder);
|
||||
}
|
||||
|
||||
/* ====== Actions ===== */
|
||||
|
||||
// Install service
|
||||
|
@@ -21,7 +21,7 @@
|
||||
<package id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.Hosting" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.StaticFiles" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
|
||||
<package id="NLog" version="4.2.3" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
||||
<package id="NLog" version="4.3.7" targetFramework="net45" />
|
||||
<package id="Owin" version="1.0" targetFramework="net45" />
|
||||
</packages>
|
@@ -23,7 +23,7 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
@@ -75,12 +75,12 @@
|
||||
<HintPath>..\packages\Microsoft.Owin.StaticFiles.3.0.1\lib\net45\Microsoft.Owin.StaticFiles.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.3.7\lib\net45\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
|
||||
|
@@ -17,7 +17,7 @@
|
||||
<package id="Microsoft.Owin.Host.HttpListener" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.Hosting" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.StaticFiles" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
|
||||
<package id="NLog" version="4.2.3" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
||||
<package id="NLog" version="4.3.7" targetFramework="net45" />
|
||||
<package id="Owin" version="1.0" targetFramework="net45" />
|
||||
</packages>
|
@@ -62,12 +62,12 @@
|
||||
<HintPath>..\packages\CsQuery.1.3.4\lib\net40\CsQuery.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="FluentAssertions, Version=4.1.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.4.1.1\lib\net45\FluentAssertions.dll</HintPath>
|
||||
<Reference Include="FluentAssertions, Version=4.13.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.4.13.0\lib\net45\FluentAssertions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="FluentAssertions.Core, Version=4.1.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.4.1.1\lib\net45\FluentAssertions.Core.dll</HintPath>
|
||||
<Reference Include="FluentAssertions.Core, Version=4.13.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.4.13.0\lib\net45\FluentAssertions.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNet.Identity.Core, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
@@ -87,12 +87,12 @@
|
||||
<HintPath>..\packages\Microsoft.Owin.Hosting.3.0.1\lib\net45\Microsoft.Owin.Hosting.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.3.7\lib\net45\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.core, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
@@ -103,8 +103,8 @@
|
||||
<HintPath>..\packages\NUnitTestAdapter.2.0.0\lib\nunit.core.interfaces.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.0.5813.39031, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.0.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Reference Include="nunit.framework, Version=3.4.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.4.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.util, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
|
@@ -42,5 +42,10 @@ namespace JackettTest
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void InitCardigannIndexers(string path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="AutoMapper" publicKeyToken="be96cd2c38ef1005" culture="neutral" />
|
||||
|
@@ -6,7 +6,7 @@
|
||||
<package id="Autofac.WebApi2.Owin" version="3.3.0" targetFramework="net45" />
|
||||
<package id="AutoMapper" version="4.1.1" targetFramework="net45" />
|
||||
<package id="CsQuery" version="1.3.4" targetFramework="net45" />
|
||||
<package id="FluentAssertions" version="4.1.1" targetFramework="net45" />
|
||||
<package id="FluentAssertions" version="4.13.0" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net45" />
|
||||
@@ -18,9 +18,9 @@
|
||||
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.Host.HttpListener" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.Hosting" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
|
||||
<package id="NLog" version="4.2.3" targetFramework="net45" />
|
||||
<package id="NUnit" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
||||
<package id="NLog" version="4.3.7" targetFramework="net45" />
|
||||
<package id="NUnit" version="3.4.1" targetFramework="net45" />
|
||||
<package id="NUnitTestAdapter" version="2.0.0" targetFramework="net45" />
|
||||
<package id="Owin" version="1.0" targetFramework="net45" />
|
||||
</packages>
|
@@ -23,7 +23,7 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="AutoMapper" publicKeyToken="be96cd2c38ef1005" culture="neutral" />
|
||||
|
@@ -79,12 +79,12 @@
|
||||
<HintPath>..\packages\Microsoft.Owin.StaticFiles.3.0.1\lib\net45\Microsoft.Owin.StaticFiles.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.3.7\lib\net45\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
|
||||
|
@@ -18,7 +18,7 @@
|
||||
<package id="Microsoft.Owin.Host.HttpListener" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.Hosting" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.StaticFiles" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
|
||||
<package id="NLog" version="4.2.3" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
||||
<package id="NLog" version="4.3.7" targetFramework="net45" />
|
||||
<package id="Owin" version="1.0" targetFramework="net45" />
|
||||
</packages>
|
@@ -23,7 +23,7 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
@@ -48,7 +48,7 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.3.7\lib\net45\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
|
@@ -5,5 +5,5 @@
|
||||
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />
|
||||
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" />
|
||||
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
|
||||
<package id="NLog" version="4.2.3" targetFramework="net45" />
|
||||
<package id="NLog" version="4.3.7" targetFramework="net45" />
|
||||
</packages>
|
@@ -9,7 +9,7 @@
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
|
@@ -200,9 +200,35 @@ function populateConfigItems(configForm, config) {
|
||||
var template = setupItemTemplate(item);
|
||||
$formItemContainer.append(template);
|
||||
if (item.type === 'recaptcha') {
|
||||
grecaptcha.render($('.jackettrecaptcha')[0], {
|
||||
'sitekey': item.sitekey
|
||||
});
|
||||
var jackettrecaptcha = $('.jackettrecaptcha');
|
||||
jackettrecaptcha.data("version", item.version);
|
||||
switch (item.version) {
|
||||
case "1":
|
||||
// The v1 reCAPTCHA code uses document.write() calls to write the CAPTCHA to the location where the script was loaded.
|
||||
// As it's loaded async this doesn't work.
|
||||
// We use an iframe to work around this problem.
|
||||
var html = '<script type="text/javascript" src="https://www.google.com/recaptcha/api/challenge?k='+encodeURIComponent(item.sitekey)+'"></script>';
|
||||
var frame = document.createElement('iframe');
|
||||
frame.id = "jackettrecaptchaiframe";
|
||||
frame.style.height = "145px";
|
||||
frame.style.weight = "326px";
|
||||
frame.style.border = "none";
|
||||
frame.onload = function () {
|
||||
// auto resize iframe to content
|
||||
frame.style.height = frame.contentWindow.document.body.scrollHeight + 'px';
|
||||
frame.style.width = frame.contentWindow.document.body.scrollWidth + 'px';
|
||||
}
|
||||
jackettrecaptcha.append(frame);
|
||||
frame.contentDocument.open();
|
||||
frame.contentDocument.write(html);
|
||||
frame.contentDocument.close();
|
||||
break;
|
||||
case "2":
|
||||
grecaptcha.render(jackettrecaptcha[0], {
|
||||
'sitekey': item.sitekey
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -235,7 +261,18 @@ function getConfigModalJson(configForm) {
|
||||
break;
|
||||
case "recaptcha":
|
||||
if (window.jackettIsLocal) {
|
||||
itemEntry.value = $('.g-recaptcha-response').val();
|
||||
var version = $el.find('.jackettrecaptcha').data("version");
|
||||
switch (version) {
|
||||
case "1":
|
||||
var frameDoc = $("#jackettrecaptchaiframe")[0].contentDocument;
|
||||
itemEntry.version = version;
|
||||
itemEntry.challenge = $("#recaptcha_challenge_field", frameDoc).val()
|
||||
itemEntry.value = $("#recaptcha_response_field", frameDoc).val()
|
||||
break;
|
||||
case "2":
|
||||
itemEntry.value = $('.g-recaptcha-response').val();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
itemEntry.cookie = $el.find(".setup-item-recaptcha input").val();
|
||||
}
|
||||
@@ -308,6 +345,32 @@ function clearNotifications() {
|
||||
$('[data-notify="container"]').remove();
|
||||
}
|
||||
|
||||
function updateReleasesRow(row)
|
||||
{
|
||||
var labels = $(row).find("span.release-labels");
|
||||
var DownloadVolumeFactor = parseFloat($(row).find("td.DownloadVolumeFactor").html());
|
||||
var UploadVolumeFactor = parseFloat($(row).find("td.UploadVolumeFactor").html());
|
||||
|
||||
labels.empty();
|
||||
|
||||
if (!isNaN(DownloadVolumeFactor)) {
|
||||
if (DownloadVolumeFactor == 0) {
|
||||
labels.append('\n<span class="label label-success">FREELEECH</span>');
|
||||
} else if (DownloadVolumeFactor < 1) {
|
||||
labels.append('\n<span class="label label-primary">' + DownloadVolumeFactor * 100 + '%DL</span>');
|
||||
} else if (DownloadVolumeFactor > 1) {
|
||||
labels.append('\n<span class="label label-danger">' + DownloadVolumeFactor * 100 + '%DL</span>');
|
||||
}
|
||||
}
|
||||
|
||||
if (!isNaN(UploadVolumeFactor)) {
|
||||
if (UploadVolumeFactor == 0) {
|
||||
labels.append('\n<span class="label label-warning">NO UPLOAD</span>');
|
||||
} else if (UploadVolumeFactor != 1) {
|
||||
labels.append('\n<span class="label label-info">' + UploadVolumeFactor * 100 + '%UL</span>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function bindUIButtons() {
|
||||
$('body').on('click', '.downloadlink', function (e, b) {
|
||||
@@ -335,6 +398,7 @@ function bindUIButtons() {
|
||||
var releaseTemplate = Handlebars.compile($("#jackett-releases").html());
|
||||
var item = { releases: data, Title: 'Releases' };
|
||||
var releaseDialog = $(releaseTemplate(item));
|
||||
releaseDialog.find('tr.jackett-releases-row').each(function () { updateReleasesRow(this); });
|
||||
releaseDialog.find('table').DataTable(
|
||||
{
|
||||
"pageLength": 20,
|
||||
@@ -382,7 +446,7 @@ function bindUIButtons() {
|
||||
var count = 0;
|
||||
this.api().columns().every(function () {
|
||||
count++;
|
||||
if (count === 5 || count === 9) {
|
||||
if (count === 5 || count === 10) {
|
||||
var column = this;
|
||||
var select = $('<select><option value=""></option></select>')
|
||||
.appendTo($(column.footer()).empty())
|
||||
@@ -430,6 +494,10 @@ function bindUIButtons() {
|
||||
var releaseDialog = $(releaseTemplate({ indexers: indexers }));
|
||||
$("#modals").append(releaseDialog);
|
||||
releaseDialog.modal("show");
|
||||
|
||||
releaseDialog.on('shown.bs.modal', function() {
|
||||
releaseDialog.find('#searchquery').focus();
|
||||
});
|
||||
|
||||
var setCategories = function (tracker, items) {
|
||||
var cats = {};
|
||||
@@ -455,6 +523,13 @@ function bindUIButtons() {
|
||||
setCategories(trackerId, this.items);
|
||||
}, scope));
|
||||
|
||||
document.getElementById("searchquery")
|
||||
.addEventListener("keyup", function (event) {
|
||||
event.preventDefault();
|
||||
if (event.keyCode == 13) {
|
||||
document.getElementById("jackett-search-perform").click();
|
||||
}
|
||||
});
|
||||
|
||||
$('#jackett-search-perform').click(function () {
|
||||
if ($('#jackett-search-perform').text().trim() !== 'Search trackers') {
|
||||
@@ -474,6 +549,7 @@ function bindUIButtons() {
|
||||
var resultsTemplate = Handlebars.compile($("#jackett-search-results").html());
|
||||
var results = $('#searchResults');
|
||||
results.html($(resultsTemplate(data)));
|
||||
results.find('tr.jackett-search-results-row').each(function () { updateReleasesRow(this); });
|
||||
|
||||
results.find('table').DataTable(
|
||||
{
|
||||
@@ -510,7 +586,7 @@ function bindUIButtons() {
|
||||
var count = 0;
|
||||
this.api().columns().every(function () {
|
||||
count++;
|
||||
if (count === 3 || count === 7) {
|
||||
if (count === 3 || count === 8) {
|
||||
var column = this;
|
||||
var select = $('<select><option value=""></option></select>')
|
||||
.appendTo($(column.footer()).empty())
|
||||
@@ -622,4 +698,4 @@ function bindUIButtons() {
|
||||
doNotify("Request to Jackett server failed", "danger", "glyphicon glyphicon-alert");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -236,26 +236,34 @@
|
||||
<th>Name</th>
|
||||
<th>Size</th>
|
||||
<th>Size</th>
|
||||
<th>Files</th>
|
||||
<th>Category</th>
|
||||
<th>Grabs</th>
|
||||
<th>Seeds</th>
|
||||
<th>Leechers</th>
|
||||
<th>DL Factor</th>
|
||||
<th>UL Factor</th>
|
||||
<th>Download</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each releases}}
|
||||
<tr>
|
||||
<tr class="jackett-releases-row">
|
||||
<td>{{PublishDate}}</td>
|
||||
<td>{{FirstSeen}}</td>
|
||||
<td>{{jacketTimespan PublishDate}}</td>
|
||||
<td>{{jacketTimespan FirstSeen}}</td>
|
||||
<td>{{Tracker}}</td>
|
||||
<td><a href="{{Comments}}">{{Title}}</a></td>
|
||||
<td><a href="{{Comments}}">{{Title}}</a> <span class="release-labels"></span></td>
|
||||
<td>{{Size}}</td>
|
||||
<td>{{jacketSize Size}}</td>
|
||||
<td>{{Files}}</td>
|
||||
<td>{{CategoryDesc}}</td>
|
||||
<td>{{Grabs}}</td>
|
||||
<td>{{Seeders}}</td>
|
||||
<td>{{Peers}}</td>
|
||||
<td class="DownloadVolumeFactor">{{DownloadVolumeFactor}}</td>
|
||||
<td class="UploadVolumeFactor">{{UploadVolumeFactor}}</td>
|
||||
<td class="downloadcolumn">
|
||||
<a class="downloadlink" title="Download locally" href="{{Link}}"><i class="fa fa-download"></i></a>
|
||||
{{#if BlackholeLink}}
|
||||
@@ -277,6 +285,12 @@
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
@@ -334,24 +348,32 @@
|
||||
<th>Name</th>
|
||||
<th>Size</th>
|
||||
<th>Size</th>
|
||||
<th>Files</th>
|
||||
<th>Category</th>
|
||||
<th>Grabs</th>
|
||||
<th>Seeds</th>
|
||||
<th>Leechers</th>
|
||||
<th>DL Factor</th>
|
||||
<th>UL Factor</th>
|
||||
<th>Download</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each Results}}
|
||||
<tr>
|
||||
<tr class="jackett-search-results-row">
|
||||
<td>{{PublishDate}}</td>
|
||||
<td>{{jacketTimespan PublishDate}}</td>
|
||||
<td>{{Tracker}}</td>
|
||||
<td><a href="{{Comments}}">{{Title}}</a></td>
|
||||
<td><a href="{{Comments}}">{{Title}}</a> <span class="release-labels"></span></td>
|
||||
<td>{{Size}}</td>
|
||||
<td>{{jacketSize Size}}</td>
|
||||
<td>{{Files}}</td>
|
||||
<td>{{CategoryDesc}}</td>
|
||||
<td>{{Grabs}}</td>
|
||||
<td>{{Seeders}}</td>
|
||||
<td>{{Peers}}</td>
|
||||
<td class="DownloadVolumeFactor">{{DownloadVolumeFactor}}</td>
|
||||
<td class="UploadVolumeFactor">{{UploadVolumeFactor}}</td>
|
||||
<td class="downloadcolumn">
|
||||
<a class="downloadlink" title="Download locally" href="{{Link}}"><i class="fa fa-download"></i></a>
|
||||
{{#if BlackholeLink}}
|
||||
@@ -371,6 +393,12 @@
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
BIN
src/Jackett/Content/logos/andraste.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
src/Jackett/Content/logos/bakabt.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
src/Jackett/Content/logos/bestfriends.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
src/Jackett/Content/logos/bitcityreloaded.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
src/Jackett/Content/logos/cinemaz.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
src/Jackett/Content/logos/digitalhive.png
Normal file
After Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 25 KiB |
BIN
src/Jackett/Content/logos/funfile.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
src/Jackett/Content/logos/fuzer.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
src/Jackett/Content/logos/ghostcity.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/Jackett/Content/logos/hdme.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
BIN
src/Jackett/Content/logos/hebits.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
src/Jackett/Content/logos/houseoftorrents.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
src/Jackett/Content/logos/myamity.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
src/Jackett/Content/logos/myanonamouse.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
src/Jackett/Content/logos/newrealworld.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
src/Jackett/Content/logos/passthepopcorn.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
src/Jackett/Content/logos/piratethenet.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
src/Jackett/Content/logos/scenefz.png
Normal file
After Width: | Height: | Size: 9.0 KiB |
BIN
src/Jackett/Content/logos/torrentheaven.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
src/Jackett/Content/logos/torrentnetwork.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/Jackett/Content/logos/torrentsyndikat.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
src/Jackett/Content/logos/x264.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
src/Jackett/Content/logos/xthor.png
Normal file
After Width: | Height: | Size: 51 KiB |
@@ -99,7 +99,9 @@ namespace Jackett.Controllers
|
||||
{
|
||||
ApiKey = request.passkey,
|
||||
Categories = MOVIE_CATS,
|
||||
SearchTerm = request.search
|
||||
SearchTerm = request.search,
|
||||
ImdbID = request.imdbid,
|
||||
QueryType = "TorrentPotato"
|
||||
};
|
||||
|
||||
IEnumerable<ReleaseInfo> releases = new List<ReleaseInfo>();
|
||||
|
101
src/Jackett/Definitions/hdme.yml
Normal file
@@ -0,0 +1,101 @@
|
||||
---
|
||||
site: hdme
|
||||
name: HDME
|
||||
language: en-us
|
||||
links:
|
||||
- https://hdme.eu
|
||||
|
||||
caps:
|
||||
categories:
|
||||
24: TV/Anime # Anime
|
||||
25: PC/0day # Appz
|
||||
47: Movies/HD # AVCHD
|
||||
26: Movies/BluRay # Bluray
|
||||
54: Movies/HD # dbREMUX
|
||||
41: Movies/HD # Documentaries
|
||||
50: Movies/HD # FourGHD
|
||||
44: Movies/HD # HDME
|
||||
28: Audio/Lossless # HQ Music
|
||||
48: Movies/HD # iCandy
|
||||
45: Movies/HD # INtL
|
||||
29: Other # Misc
|
||||
49: PC/Phone-Other # Mobile
|
||||
30: Movies/HD # Movie 1080i
|
||||
31: Movies/HD # Movie 1080p
|
||||
32: Movies/HD # Movie 720p
|
||||
33: Audio/Video # Music Videos
|
||||
34: TV # Packs
|
||||
53: Movies/HD # Remux
|
||||
56: Movies/HD # RUXi
|
||||
55: Movies/HD # SiNiSteR
|
||||
36: TV/Sport # Sports
|
||||
37: TV/HD # TV Series 1080i
|
||||
38: TV/HD # TV Series 1080p
|
||||
39: TV/HD # TV Series 720p
|
||||
57: Movies # UHD 2160p
|
||||
40: XXX # XXX
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: /takelogin.php
|
||||
method: post
|
||||
form: form
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
error:
|
||||
- selector: td.embedded
|
||||
message:
|
||||
selector: td.text
|
||||
test:
|
||||
path: /my.php
|
||||
|
||||
ratio:
|
||||
path: /my.php
|
||||
selector: span.smallfont > font
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "Ratio:(.+?)Uploaded"
|
||||
- name: replace
|
||||
args: [",", ""]
|
||||
|
||||
search:
|
||||
path: /browse.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
incldead: "1"
|
||||
blah: "0"
|
||||
rows:
|
||||
selector: table[width="100%"] > tbody > tr:has(td.bottom[background="_images/bg_torrent.jpg"])
|
||||
fields:
|
||||
category:
|
||||
selector: td:nth-child(2) a
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
title:
|
||||
selector: td:nth-child(3) > a
|
||||
attribute: title
|
||||
comments:
|
||||
selector: td:nth-child(3) > a
|
||||
attribute: href
|
||||
download:
|
||||
selector: td:nth-child(11) > a
|
||||
attribute: href
|
||||
size:
|
||||
selector: td:nth-child(6)
|
||||
remove: br
|
||||
date:
|
||||
selector: td:nth-child(3)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "Added: (.+?)\n"
|
||||
seeders:
|
||||
selector: td:nth-child(8)
|
||||
leechers:
|
||||
selector: td:nth-child(9)
|
@@ -52,13 +52,13 @@ namespace Jackett.Indexers
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var loginPage = await RequestStringWithCookies(LoginUrl, string.Empty);
|
||||
var token = new Regex("Avz.CSRF_TOKEN = '(.*?)';").Match(loginPage.Content).Groups[1].ToString();
|
||||
var token = new Regex("<meta name=\"_token\" content=\"(.*?)\">").Match(loginPage.Content).Groups[1].ToString();
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "_token", token },
|
||||
{ "username_email", configData.Username.Value },
|
||||
{ "email_username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "remember", "on" }
|
||||
};
|
||||
{ "remember", "1" }
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, loginPage.Cookies, true, null, LoginUrl);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("auth/logout"), () =>
|
||||
@@ -91,7 +91,7 @@ namespace Jackett.Indexers
|
||||
try
|
||||
{
|
||||
CQ dom = response.Content;
|
||||
var rows = dom["table > tbody > tr"];
|
||||
var rows = dom["table:has(thead) > tbody > tr"];
|
||||
foreach (var row in rows)
|
||||
{
|
||||
CQ qRow = row.Cq();
|
||||
@@ -100,29 +100,47 @@ namespace Jackett.Indexers
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800;
|
||||
|
||||
var qLink = row.ChildElements.ElementAt(1).FirstElementChild.Cq();
|
||||
var qLink = qRow.Find("a.torrent-filename"); ;
|
||||
release.Title = qLink.Text().Trim();
|
||||
release.Comments = new Uri(qLink.Attr("href"));
|
||||
release.Guid = release.Comments;
|
||||
|
||||
var qDownload = row.ChildElements.ElementAt(3).FirstElementChild.Cq();
|
||||
var qDownload = qRow.Find("a.torrent-download-icon"); ;
|
||||
release.Link = new Uri(qDownload.Attr("href"));
|
||||
|
||||
var dateStr = row.ChildElements.ElementAt(5).Cq().Text().Trim();
|
||||
var dateStr = qRow.Find("td:eq(3) > span").Text().Trim();
|
||||
release.PublishDate = DateTimeUtil.FromTimeAgo(dateStr);
|
||||
|
||||
var sizeStr = row.ChildElements.ElementAt(6).Cq().Text();
|
||||
var sizeStr = qRow.Find("td:eq(5) > span").Text().Trim();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(row.ChildElements.ElementAt(8).Cq().Text());
|
||||
release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(9).Cq().Text()) + release.Seeders;
|
||||
release.Seeders = ParseUtil.CoerceInt(qRow.Find("td:eq(6)").Text().Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(qRow.Find("td:eq(7)").Text().Trim()) + release.Seeders;
|
||||
|
||||
var cat = row.Cq().Find("td:eq(0) i").First().Attr("class")
|
||||
.Replace("gi gi-film", "1")
|
||||
.Replace("gi gi-tv", "2")
|
||||
.Replace("gi gi-music", "3")
|
||||
.Replace("torrent-icon", string.Empty)
|
||||
.Replace("fa fa-", string.Empty)
|
||||
.Replace("film", "1")
|
||||
.Replace("tv", "2")
|
||||
.Replace("music", "3")
|
||||
.Replace("text-pink", string.Empty);
|
||||
release.Category = MapTrackerCatToNewznab(cat.Trim());
|
||||
|
||||
var grabs = row.Cq().Find("td:nth-child(9)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (row.Cq().Find("i.fa-star").Any())
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else if (row.Cq().Find("i.fa-star-half-o").Any())
|
||||
release.DownloadVolumeFactor = 0.5;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
if (row.Cq().Find("i.fa-diamond").Any())
|
||||
release.UploadVolumeFactor = 2;
|
||||
else
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
@@ -30,7 +30,7 @@ namespace Jackett.Indexers
|
||||
: base(name: "AlphaRatio",
|
||||
description: "Legendary",
|
||||
link: "https://alpharatio.cc/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: w,
|
||||
logger: l,
|
||||
@@ -56,13 +56,18 @@ namespace Jackett.Indexers
|
||||
AddCategoryMapping(23, TorznabCatType.Audio);
|
||||
}
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
var incomingConfig = new ConfigurationDataBasicLogin();
|
||||
incomingConfig.LoadValuesFromJson(configJson);
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", incomingConfig.Username.Value },
|
||||
{ "password", incomingConfig.Password.Value },
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "login", "Login" },
|
||||
{ "keeplogged", "1" }
|
||||
};
|
||||
@@ -74,7 +79,7 @@ namespace Jackett.Indexers
|
||||
CQ dom = response.Content;
|
||||
dom["#loginform > table"].Remove();
|
||||
var errorMessage = dom["#loginform"].Text().Trim().Replace("\n\t", " ");
|
||||
throw new ExceptionWithConfigData(errorMessage, (ConfigurationData)incomingConfig);
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
@@ -88,6 +93,25 @@ namespace Jackett.Indexers
|
||||
release.Guid = new Uri(GuidUrl + id);
|
||||
release.Comments = release.Guid;
|
||||
release.Link = new Uri(DownloadUrl + id);
|
||||
release.Category = MapTrackerCatToNewznab(CategoryReverseMapper((string)r["category"]));
|
||||
release.Files = (int)r["fileCount"];
|
||||
release.Grabs = (int)r["snatches"];
|
||||
release.DownloadVolumeFactor = 1;
|
||||
release.UploadVolumeFactor = 1;
|
||||
if ((bool)r["isFreeleech"])
|
||||
{
|
||||
release.DownloadVolumeFactor = 0;
|
||||
}
|
||||
if ((bool)r["isPersonalFreeleech"])
|
||||
{
|
||||
release.DownloadVolumeFactor = 0;
|
||||
}
|
||||
if ((bool)r["isNeutralLeech"])
|
||||
{
|
||||
release.DownloadVolumeFactor = 0;
|
||||
release.UploadVolumeFactor = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
@@ -159,7 +183,23 @@ namespace Jackett.Indexers
|
||||
{
|
||||
DateTime unixStart = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
|
||||
long unixTimeStampInTicks = (long)(unixTime * TimeSpan.TicksPerSecond);
|
||||
return new DateTime(unixStart.Ticks + unixTimeStampInTicks);
|
||||
return new DateTime(unixStart.Ticks + unixTimeStampInTicks, DateTimeKind.Utc).ToLocalTime();
|
||||
}
|
||||
|
||||
static string CategoryReverseMapper(string categoryName)
|
||||
{
|
||||
Dictionary<string, string> dictionary = new Dictionary<string, string>();
|
||||
|
||||
dictionary.Add("TvSD", "1");
|
||||
dictionary.Add("TvHD", "2");
|
||||
dictionary.Add("MovieSD", "6");
|
||||
dictionary.Add("MovieHD", "7");
|
||||
|
||||
if (dictionary.ContainsKey(categoryName))
|
||||
{
|
||||
return dictionary[categoryName];
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
205
src/Jackett/Indexers/Andraste.cs
Normal file
@@ -0,0 +1,205 @@
|
||||
using Jackett.Utils.Clients;
|
||||
using NLog;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using CsQuery;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class Andraste : BaseIndexer, IIndexer
|
||||
{
|
||||
string LoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
string BrowseUrl { get { return SiteLink + "browse.php"; } }
|
||||
|
||||
new ConfigurationDataBasicLoginWithRSSAndDisplay configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLoginWithRSSAndDisplay)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public Andraste(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "Andraste",
|
||||
description: "A German general tracker.",
|
||||
link: "https://andraste.io/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLoginWithRSSAndDisplay())
|
||||
{
|
||||
AddCategoryMapping(9, TorznabCatType.Other); // Anderes
|
||||
AddCategoryMapping(23, TorznabCatType.TVAnime); // Animation - Film &; Serie
|
||||
AddCategoryMapping(1, TorznabCatType.PC); // Appz
|
||||
AddCategoryMapping(52, TorznabCatType.Other); // Botuploads
|
||||
AddCategoryMapping(25, TorznabCatType.TVDocumentary); // Doku - Alle Formate
|
||||
AddCategoryMapping(27, TorznabCatType.Books); // E-Books
|
||||
AddCategoryMapping(51, TorznabCatType.Movies3D); // Film/3D
|
||||
AddCategoryMapping(20, TorznabCatType.MoviesDVD); // Film/DVDr
|
||||
AddCategoryMapping(37, TorznabCatType.MoviesHD); // Film/HD 1080p++
|
||||
AddCategoryMapping(38, TorznabCatType.MoviesSD); // Film/HD 720p
|
||||
AddCategoryMapping(36, TorznabCatType.Movies); // Film/im Kino
|
||||
AddCategoryMapping(19, TorznabCatType.Movies); // Film/XviD,DivX,x264
|
||||
AddCategoryMapping(4, TorznabCatType.PCGames); // Games/PC
|
||||
AddCategoryMapping(12, TorznabCatType.ConsolePS4); // Games/Playstation
|
||||
AddCategoryMapping(22, TorznabCatType.ConsoleWii); // Games/Wii & DS
|
||||
AddCategoryMapping(21, TorznabCatType.ConsoleXbox); // Games/Xbox & 360
|
||||
AddCategoryMapping(48, TorznabCatType.PCPhoneAndroid); // Handy & PDA/Android
|
||||
AddCategoryMapping(47, TorznabCatType.PCPhoneIOS); // Handy & PDA/iOS
|
||||
AddCategoryMapping(44, TorznabCatType.PCMac); // Macintosh
|
||||
AddCategoryMapping(41, TorznabCatType.Other); // MegaPack
|
||||
AddCategoryMapping(24, TorznabCatType.AudioAudiobook); // Musik/Hörbuch & Hörspiel
|
||||
AddCategoryMapping(46, TorznabCatType.Audio); // Musik/HQ 320++
|
||||
AddCategoryMapping(6, TorznabCatType.Audio); // Musik/Musik
|
||||
AddCategoryMapping(26, TorznabCatType.AudioVideo); // Musik/Musikvideos
|
||||
AddCategoryMapping(29, TorznabCatType.TVSD); // Serien/DVDr
|
||||
AddCategoryMapping(35, TorznabCatType.TVHD); // Serien/HD 720p++
|
||||
AddCategoryMapping(7, TorznabCatType.TV); // Serien/XviD,DivX,x264
|
||||
AddCategoryMapping(45, TorznabCatType.TV); // Shows
|
||||
AddCategoryMapping(40, TorznabCatType.TVSport); // Sport
|
||||
AddCategoryMapping(32, TorznabCatType.XXX); // XXX
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
var pairs = new Dictionary<string, string>
|
||||
{
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value }
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, LoginUrl, true);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var errorMessage = dom["table.tableinborder"].Html();
|
||||
errorMessage = result.Content;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
TimeZoneInfo.TransitionTime startTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 3, 0, 0), 3, 5, DayOfWeek.Sunday);
|
||||
TimeZoneInfo.TransitionTime endTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 4, 0, 0), 10, 5, DayOfWeek.Sunday);
|
||||
TimeSpan delta = new TimeSpan(1, 0, 0);
|
||||
TimeZoneInfo.AdjustmentRule adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(1999, 10, 1), DateTime.MaxValue.Date, delta, startTransition, endTransition);
|
||||
TimeZoneInfo.AdjustmentRule[] adjustments = { adjustment };
|
||||
TimeZoneInfo germanyTz = TimeZoneInfo.CreateCustomTimeZone("W. Europe Standard Time", new TimeSpan(1, 0, 0), "(GMT+01:00) W. Europe Standard Time", "W. Europe Standard Time", "W. Europe DST Time", adjustments);
|
||||
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = BrowseUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
queryCollection.Add("showsearch", "1");
|
||||
queryCollection.Add("incldead", "1");
|
||||
queryCollection.Add("orderby", "added");
|
||||
queryCollection.Add("sort", "desc");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
queryCollection.Add("search", searchString);
|
||||
}
|
||||
|
||||
foreach (var cat in MapTorznabCapsToTrackers(query))
|
||||
{
|
||||
queryCollection.Add("c" + cat, "1");
|
||||
}
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
var response = await RequestBytesWithCookies(searchUrl);
|
||||
var results = Encoding.GetEncoding("iso-8859-1").GetString(response.Content);
|
||||
try
|
||||
{
|
||||
CQ dom = results;
|
||||
var rows = dom["table.tableinborder > tbody > tr:has(td.tableb)"];
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
var qRow = row.Cq();
|
||||
|
||||
var qDetailsLink = qRow.Find("a[href^=details.php?id=]").First();
|
||||
release.Title = qDetailsLink.Attr("title");
|
||||
|
||||
if (!query.MatchQueryStringAND(release.Title))
|
||||
continue;
|
||||
|
||||
var qCatLink = qRow.Find("a[href^=browse.php?cat=]").First();
|
||||
var qDLLink = qRow.Find("a[href^=download.php?torrent=]").First();
|
||||
var qSeeders = qRow.Find("span:contains(Seeder) > b:eq(0)");
|
||||
var qLeechers = qRow.Find("span:contains(Seeder) > b:eq(1)");
|
||||
var qDateStr = qRow.Find("td > table > tbody > tr > td:eq(7)").First();
|
||||
var qSize = qRow.Find("span:contains(Volumen) > b:eq(0)").First();
|
||||
var qOnlyUpload = qRow.Find("img[title=OnlyUpload]");
|
||||
|
||||
if(qOnlyUpload.Any())
|
||||
{
|
||||
release.MinimumRatio = 2;
|
||||
release.MinimumSeedTime = 144 * 60 * 60;
|
||||
}
|
||||
else
|
||||
{
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 72 * 60 * 60;
|
||||
}
|
||||
|
||||
var catStr = qCatLink.Attr("href").Split('=')[1];
|
||||
release.Category = MapTrackerCatToNewznab(catStr);
|
||||
|
||||
release.Link = new Uri(SiteLink + qDLLink.Attr("href"));
|
||||
release.Comments = new Uri(SiteLink + qDetailsLink.Attr("href"));
|
||||
release.Guid = release.Link;
|
||||
|
||||
var sizeStr = qSize.Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(qSeeders.Text());
|
||||
release.Peers = ParseUtil.CoerceInt(qLeechers.Text()) + release.Seeders;
|
||||
|
||||
var dateStr = qDateStr.Text().Trim().Replace('\xA0', ' ');
|
||||
DateTime dateGerman = DateTime.SpecifyKind(DateTime.ParseExact(dateStr, "dd.MM.yyyy HH:mm", CultureInfo.InvariantCulture), DateTimeKind.Unspecified);
|
||||
|
||||
DateTime pubDateUtc = TimeZoneInfo.ConvertTimeToUtc(dateGerman, germanyTz);
|
||||
release.PublishDate = pubDateUtc.ToLocalTime();
|
||||
|
||||
var files = qRow.Find("a[href*=\"&filelist=1\"] ~ font ~ b").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = qRow.Find("a[href*=\"&tosnatchers=1\"] ~ font ~ b").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (qRow.Find("img[alt=\"OU\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,8 +24,15 @@ namespace Jackett.Indexers
|
||||
{
|
||||
public class AnimeBytes : BaseIndexer, IIndexer
|
||||
{
|
||||
enum SearchType
|
||||
{
|
||||
Video,
|
||||
Audio
|
||||
}
|
||||
|
||||
private string LoginUrl { get { return SiteLink + "user/login"; } }
|
||||
private string SearchUrl { get { return SiteLink + "torrents.php?"; } }
|
||||
private string MusicSearchUrl { get { return SiteLink + "torrents2.php?"; } }
|
||||
public bool AllowRaws { get { return configData.IncludeRaw.Value; } }
|
||||
public bool InsertSeason { get { return configData.InsertSeason!=null && configData.InsertSeason.Value; } }
|
||||
|
||||
@@ -46,7 +53,10 @@ namespace Jackett.Indexers
|
||||
TorznabCatType.BooksComics,
|
||||
TorznabCatType.ConsolePSP,
|
||||
TorznabCatType.ConsoleOther,
|
||||
TorznabCatType.PCGames),
|
||||
TorznabCatType.PCGames,
|
||||
TorznabCatType.AudioMP3,
|
||||
TorznabCatType.AudioLossless,
|
||||
TorznabCatType.AudioOther),
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataAnimeBytes())
|
||||
@@ -55,7 +65,7 @@ namespace Jackett.Indexers
|
||||
}
|
||||
|
||||
|
||||
public IEnumerable<ReleaseInfo> FilterResults(TorznabQuery query, IEnumerable<ReleaseInfo> input)
|
||||
public override IEnumerable<ReleaseInfo> FilterResults(TorznabQuery query, IEnumerable<ReleaseInfo> input)
|
||||
{
|
||||
// Prevent filtering
|
||||
return input;
|
||||
@@ -139,8 +149,16 @@ namespace Jackett.Indexers
|
||||
{
|
||||
// The result list
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
if (ContainsMusicCategories(query.Categories))
|
||||
{
|
||||
foreach (var result in await GetResults(SearchType.Audio, query.SanitizedSearchTerm))
|
||||
{
|
||||
releases.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var result in await GetResults(StripEpisodeNumber(query.SanitizedSearchTerm)))
|
||||
foreach (var result in await GetResults(SearchType.Video, StripEpisodeNumber(query.SanitizedSearchTerm)))
|
||||
{
|
||||
releases.Add(result);
|
||||
}
|
||||
@@ -148,31 +166,45 @@ namespace Jackett.Indexers
|
||||
return releases.ToArray();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> GetResults(string searchTerm)
|
||||
private bool ContainsMusicCategories(int[] categories)
|
||||
{
|
||||
var music = new[]
|
||||
{
|
||||
TorznabCatType.Audio.ID,
|
||||
TorznabCatType.AudioMP3.ID,
|
||||
TorznabCatType.AudioLossless.ID,
|
||||
TorznabCatType.AudioOther.ID,
|
||||
TorznabCatType.AudioForeign.ID
|
||||
};
|
||||
|
||||
return categories.Length == 0 || music.Any(categories.Contains);
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<ReleaseInfo>> GetResults(SearchType searchType, string searchTerm)
|
||||
{
|
||||
var cleanSearchTerm = HttpUtility.UrlEncode(searchTerm);
|
||||
|
||||
// The result list
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
var queryUrl = searchType == SearchType.Video ? SearchUrl : MusicSearchUrl;
|
||||
// Only include the query bit if its required as hopefully the site caches the non query page
|
||||
if (!string.IsNullOrWhiteSpace(searchTerm))
|
||||
{
|
||||
queryUrl += string.Format("searchstr={0}&action=advanced&search_type=title&year=&year2=&tags=&tags_type=0&sort=time_added&way=desc&hentai=2&releasegroup=&epcount=&epcount2=&artbooktitle=", cleanSearchTerm);
|
||||
}
|
||||
|
||||
// Check cache first so we don't query the server for each episode when searching for each episode in a series.
|
||||
lock (cache)
|
||||
{
|
||||
// Remove old cache items
|
||||
CleanCache();
|
||||
|
||||
var cachedResult = cache.Where(i => i.Query == searchTerm).FirstOrDefault();
|
||||
var cachedResult = cache.Where(i => i.Query == queryUrl).FirstOrDefault();
|
||||
if (cachedResult != null)
|
||||
return cachedResult.Results.Select(s => (ReleaseInfo)s.Clone()).ToArray();
|
||||
}
|
||||
|
||||
var queryUrl = SearchUrl;
|
||||
// Only include the query bit if its required as hopefully the site caches the non query page
|
||||
if (!string.IsNullOrWhiteSpace(searchTerm))
|
||||
{
|
||||
queryUrl += string.Format("searchstr={0}&action=advanced&search_type=title&year=&year2=&tags=&tags_type=0&sort=time_added&way=desc&hentai=2&releasegroup=&epcount=&epcount2=&artbooktitle=", cleanSearchTerm);
|
||||
}
|
||||
|
||||
|
||||
// Get the content from the tracker
|
||||
var response = await RequestStringWithCookiesAndRetry(queryUrl);
|
||||
CQ dom = response.Content;
|
||||
@@ -191,7 +223,11 @@ namespace Jackett.Indexers
|
||||
var seriesCq = series.Cq();
|
||||
|
||||
var synonyms = new List<string>();
|
||||
var mainTitle = seriesCq.Find(".group_title strong a").First().Text().Trim();
|
||||
string mainTitle;
|
||||
if (searchType == SearchType.Video)
|
||||
mainTitle = seriesCq.Find(".group_title strong a").First().Text().Trim();
|
||||
else
|
||||
mainTitle = seriesCq.Find(".group_title strong").Text().Trim();
|
||||
|
||||
var yearStr = seriesCq.Find(".group_title strong").First().Text().Trim().Replace("]", "").Trim();
|
||||
int yearIndex = yearStr.LastIndexOf("[");
|
||||
@@ -282,33 +318,52 @@ namespace Jackett.Indexers
|
||||
release.Guid = new Uri(SiteLink + infoLink.Attributes.GetAttribute("href") + "&nh=" + StringUtil.Hash(title)); // Sonarr should dedupe on this url - allow a url per name.
|
||||
release.Link = new Uri(downloadLink.Attributes.GetAttribute("href"), UriKind.Relative);
|
||||
|
||||
var category = seriesCq.Find("a[title=\"View Torrent\"]").Text().Trim();
|
||||
if (category == "TV Series")
|
||||
release.Category = TorznabCatType.TVAnime.ID;
|
||||
|
||||
// Ignore these categories as they'll cause hell with the matcher
|
||||
// TV Special, OVA, ONA, DVD Special, BD Special
|
||||
|
||||
if (category == "Movie")
|
||||
release.Category = TorznabCatType.Movies.ID;
|
||||
|
||||
if (category == "Manga" || category == "Oneshot" || category == "Anthology" || category == "Manhwa" || category == "Manhua" || category == "Light Novel")
|
||||
release.Category = TorznabCatType.BooksComics.ID;
|
||||
|
||||
if (category == "Novel" || category == "Artbook")
|
||||
release.Category = TorznabCatType.BooksComics.ID;
|
||||
|
||||
if (category == "Game" || category == "Visual Novel")
|
||||
string category = null;
|
||||
if (searchType == SearchType.Video)
|
||||
{
|
||||
var description = rowCq.Find(".torrent_properties a:eq(1)").Text();
|
||||
if (description.Contains(" PSP "))
|
||||
release.Category = TorznabCatType.ConsolePSP.ID;
|
||||
if (description.Contains("PSX"))
|
||||
release.Category = TorznabCatType.ConsoleOther.ID;
|
||||
if (description.Contains(" NES "))
|
||||
release.Category = TorznabCatType.ConsoleOther.ID;
|
||||
if (description.Contains(" PC "))
|
||||
release.Category = TorznabCatType.PCGames.ID;
|
||||
category = seriesCq.Find("a[title=\"View Torrent\"]").Text().Trim();
|
||||
if (category == "TV Series")
|
||||
release.Category = TorznabCatType.TVAnime.ID;
|
||||
|
||||
// Ignore these categories as they'll cause hell with the matcher
|
||||
// TV Special, OVA, ONA, DVD Special, BD Special
|
||||
|
||||
if (category == "Movie")
|
||||
release.Category = TorznabCatType.Movies.ID;
|
||||
|
||||
if (category == "Manga" || category == "Oneshot" || category == "Anthology" || category == "Manhwa" || category == "Manhua" || category == "Light Novel")
|
||||
release.Category = TorznabCatType.BooksComics.ID;
|
||||
|
||||
if (category == "Novel" || category == "Artbook")
|
||||
release.Category = TorznabCatType.BooksComics.ID;
|
||||
|
||||
if (category == "Game" || category == "Visual Novel")
|
||||
{
|
||||
var description = rowCq.Find(".torrent_properties a:eq(1)").Text();
|
||||
if (description.Contains(" PSP "))
|
||||
release.Category = TorznabCatType.ConsolePSP.ID;
|
||||
if (description.Contains("PSX"))
|
||||
release.Category = TorznabCatType.ConsoleOther.ID;
|
||||
if (description.Contains(" NES "))
|
||||
release.Category = TorznabCatType.ConsoleOther.ID;
|
||||
if (description.Contains(" PC "))
|
||||
release.Category = TorznabCatType.PCGames.ID;
|
||||
}
|
||||
}
|
||||
|
||||
if (searchType == SearchType.Audio)
|
||||
{
|
||||
category = seriesCq.Find(".group_img .cat a").Text();
|
||||
if (category == "Single" || category == "Album" || category == "Compilation" || category == "Soundtrack" || category == "Remix CD")
|
||||
{
|
||||
var description = rowCq.Find(".torrent_properties a:eq(1)").Text();
|
||||
if (description.Contains(" Lossless "))
|
||||
release.Category = TorznabCatType.AudioLossless.ID;
|
||||
else if (description.Contains("MP3"))
|
||||
release.Category = TorznabCatType.AudioMP3.ID;
|
||||
else
|
||||
release.Category = TorznabCatType.AudioOther.ID;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -371,6 +426,17 @@ namespace Jackett.Indexers
|
||||
release.Seeders = ParseUtil.CoerceInt(rowCq.Find(".torrent_seeders").Text());
|
||||
release.Peers = release.Seeders + ParseUtil.CoerceInt(rowCq.Find(".torrent_leechers").Text());
|
||||
|
||||
// grabs
|
||||
var grabs = rowCq.Find("td.torrent_snatched").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
// freeleech
|
||||
if (rowCq.Find("img[alt=\"Freeleech!\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
if (release.Category != 0)
|
||||
releases.Add(release);
|
||||
}
|
||||
@@ -386,7 +452,7 @@ namespace Jackett.Indexers
|
||||
// Add to the cache
|
||||
lock (cache)
|
||||
{
|
||||
cache.Add(new CachedQueryResult(searchTerm, releases));
|
||||
cache.Add(new CachedQueryResult(queryUrl, releases));
|
||||
}
|
||||
|
||||
return releases.Select(s => (ReleaseInfo)s.Clone());
|
||||
|
198
src/Jackett/Indexers/BakaBT.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class BakaBT : BaseIndexer, IIndexer
|
||||
{
|
||||
public string SearchUrl { get { return SiteLink + "browse.php?only=0&incomplete=1&lossless=1&hd=1&multiaudio=1&bonus=1&reorder=1&q="; } }
|
||||
public string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
public string id = "bakabt";
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public BakaBT(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "BakaBT",
|
||||
description: "Anime Comunity",
|
||||
link: "https://bakabt.me/",
|
||||
caps: new TorznabCapabilities(TorznabCatType.TVAnime),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
var loginForm = await webclient.GetString(new Utils.Clients.WebRequest()
|
||||
{
|
||||
Url = LoginUrl,
|
||||
Type = RequestType.GET
|
||||
});
|
||||
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "returnto", "/index.php" }
|
||||
};
|
||||
|
||||
var response = await RequestLoginAndFollowRedirect(LoginUrl, pairs, loginForm.Cookies, true, null, SiteLink);
|
||||
var responseContent = response.Content;
|
||||
await ConfigureIfOK(response.Cookies, responseContent.Contains("<a href=\"logout.php\">Logout</a>"), () =>
|
||||
{
|
||||
CQ dom = responseContent;
|
||||
var messageEl = dom[".error"].First();
|
||||
var errorMessage = messageEl.Text().Trim();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
|
||||
// This tracker only deals with full seasons so chop off the episode/season number if we have it D:
|
||||
if (!string.IsNullOrWhiteSpace(query.SearchTerm))
|
||||
{
|
||||
var splitindex = query.SearchTerm.LastIndexOf(' ');
|
||||
if (splitindex > -1)
|
||||
query.SearchTerm = query.SearchTerm.Substring(0, splitindex);
|
||||
}
|
||||
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchString = query.SanitizedSearchTerm;
|
||||
var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(searchString);
|
||||
var response = await RequestStringWithCookiesAndRetry(episodeSearchUrl);
|
||||
|
||||
try
|
||||
{
|
||||
CQ dom = response.Content;
|
||||
var rows = dom[".torrents tr.torrent"];
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
|
||||
var qRow = row.Cq();
|
||||
var qTitleLink = qRow.Find("a.title").First();
|
||||
var title = qTitleLink.Text().Trim();
|
||||
|
||||
// Insert before the release info
|
||||
var taidx = title.IndexOf('(');
|
||||
var tbidx = title.IndexOf('[');
|
||||
|
||||
if (taidx == -1)
|
||||
taidx = title.Length;
|
||||
|
||||
if (tbidx == -1)
|
||||
tbidx = title.Length;
|
||||
var titleSplit = Math.Min(taidx, tbidx);
|
||||
var titleSeries = title.Substring(0, titleSplit);
|
||||
var releaseInfo = title.Substring(titleSplit);
|
||||
|
||||
// For each over each pipe deliminated name
|
||||
foreach (var name in titleSeries.Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
|
||||
release.Title = (name + releaseInfo).Trim();
|
||||
// Ensure the season is defined as this tracker only deals with full seasons
|
||||
if (release.Title.IndexOf("Season") == -1)
|
||||
{
|
||||
// Insert before the release info
|
||||
var aidx = release.Title.IndexOf('(');
|
||||
var bidx = release.Title.IndexOf('[');
|
||||
|
||||
if (aidx == -1)
|
||||
aidx = release.Title.Length;
|
||||
|
||||
if (bidx == -1)
|
||||
bidx = release.Title.Length;
|
||||
|
||||
var insertPoint = Math.Min(aidx, bidx);
|
||||
release.Title = release.Title.Substring(0, insertPoint) + "Season 1 " + release.Title.Substring(insertPoint);
|
||||
}
|
||||
|
||||
release.Description = release.Title;
|
||||
release.Guid = new Uri(SiteLink + qTitleLink.Attr("href"));
|
||||
release.Comments = release.Guid;
|
||||
|
||||
release.Link = new Uri(SiteLink + qRow.Find(".peers a").First().Attr("href"));
|
||||
|
||||
release.Seeders = int.Parse(qRow.Find(".peers a").Get(0).InnerText);
|
||||
release.Peers = release.Seeders + int.Parse(qRow.Find(".peers a").Get(1).InnerText);
|
||||
|
||||
release.MinimumRatio = 1;
|
||||
|
||||
var size = qRow.Find(".size").First().Text();
|
||||
release.Size = ReleaseInfo.GetBytes(size);
|
||||
|
||||
//22 Jul 15
|
||||
var dateStr = qRow.Find(".added").First().Text().Replace("'", string.Empty);
|
||||
if (dateStr.Split(' ')[0].Length == 1)
|
||||
dateStr = "0" + dateStr;
|
||||
|
||||
if (string.Equals(dateStr, "yesterday", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
release.PublishDate = DateTime.Now.AddDays(-1);
|
||||
}
|
||||
else if (string.Equals(dateStr, "today", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
release.PublishDate = DateTime.Now;
|
||||
}
|
||||
else
|
||||
{
|
||||
release.PublishDate = DateTime.ParseExact(dateStr, "dd MMM yy", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(response.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
public override async Task<byte[]> Download(Uri link)
|
||||
{
|
||||
var downloadPage = await RequestStringWithCookies(link.ToString());
|
||||
CQ dom = downloadPage.Content;
|
||||
var downloadLink = dom.Find(".download_link").First().Attr("href");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(downloadLink))
|
||||
{
|
||||
throw new Exception("Unable to find download link.");
|
||||
}
|
||||
|
||||
var response = await RequestBytesWithCookies(SiteLink + downloadLink);
|
||||
return response.Content;
|
||||
}
|
||||
}
|
||||
}
|
@@ -17,13 +17,13 @@ namespace Jackett.Indexers
|
||||
{
|
||||
public abstract class BaseIndexer
|
||||
{
|
||||
public string SiteLink { get; private set; }
|
||||
public string DisplayDescription { get; private set; }
|
||||
public string DisplayName { get; private set; }
|
||||
public string SiteLink { get; protected set; }
|
||||
public string DisplayDescription { get; protected set; }
|
||||
public string DisplayName { get; protected set; }
|
||||
public string ID { get { return GetIndexerID(GetType()); } }
|
||||
|
||||
public bool IsConfigured { get; protected set; }
|
||||
public TorznabCapabilities TorznabCaps { get; private set; }
|
||||
public TorznabCapabilities TorznabCaps { get; protected set; }
|
||||
protected Logger logger;
|
||||
protected IIndexerManagerService indexerService;
|
||||
protected static List<CachedQueryResult> cache = new List<CachedQueryResult>();
|
||||
@@ -44,7 +44,9 @@ namespace Jackett.Indexers
|
||||
|
||||
private List<CategoryMapping> categoryMapping = new List<CategoryMapping>();
|
||||
|
||||
// standard constructor used by most indexers
|
||||
public BaseIndexer(string name, string link, string description, IIndexerManagerService manager, IWebClient client, Logger logger, ConfigurationData configData, IProtectionService p, TorznabCapabilities caps = null, string downloadBase = null)
|
||||
: this(manager, client, logger, p)
|
||||
{
|
||||
if (!link.EndsWith("/"))
|
||||
throw new Exception("Site link must end with a slash.");
|
||||
@@ -52,12 +54,7 @@ namespace Jackett.Indexers
|
||||
DisplayName = name;
|
||||
DisplayDescription = description;
|
||||
SiteLink = link;
|
||||
this.logger = logger;
|
||||
indexerService = manager;
|
||||
webclient = client;
|
||||
protectionService = p;
|
||||
this.downloadUrlBase = downloadBase;
|
||||
|
||||
this.configData = configData;
|
||||
|
||||
if (caps == null)
|
||||
@@ -66,6 +63,15 @@ namespace Jackett.Indexers
|
||||
|
||||
}
|
||||
|
||||
// minimal constructor used by e.g. cardigann generic indexer
|
||||
public BaseIndexer(IIndexerManagerService manager, IWebClient client, Logger logger, IProtectionService p)
|
||||
{
|
||||
this.logger = logger;
|
||||
indexerService = manager;
|
||||
webclient = client;
|
||||
protectionService = p;
|
||||
}
|
||||
|
||||
public IEnumerable<ReleaseInfo> CleanLinks(IEnumerable<ReleaseInfo> releases)
|
||||
{
|
||||
if (string.IsNullOrEmpty(downloadUrlBase))
|
||||
@@ -179,7 +185,7 @@ namespace Jackett.Indexers
|
||||
|
||||
private String ResolveCookies(String incomingCookies = "")
|
||||
{
|
||||
var redirRequestCookies = (CookieHeader != "" ? CookieHeader + " " : "") + incomingCookies;
|
||||
var redirRequestCookies = (CookieHeader != null && CookieHeader != "" ? CookieHeader + " " : "") + incomingCookies;
|
||||
System.Text.RegularExpressions.Regex expression = new System.Text.RegularExpressions.Regex(@"([^\s]+)=([^=]+)(?:\s|$)");
|
||||
Dictionary<string, string> cookieDIctionary = new Dictionary<string, string>();
|
||||
var matches = expression.Match(redirRequestCookies);
|
||||
@@ -192,6 +198,19 @@ namespace Jackett.Indexers
|
||||
|
||||
}
|
||||
|
||||
// Update CookieHeader with new cookies and save the config if something changed (e.g. a new CloudFlare clearance cookie was issued)
|
||||
protected void UpdateCookieHeader(string newCookies, string cookieOverride = null)
|
||||
{
|
||||
string newCookieHeader = ResolveCookies((cookieOverride != null && cookieOverride != "" ? cookieOverride + " " : "") + newCookies);
|
||||
if (CookieHeader != newCookieHeader)
|
||||
{
|
||||
logger.Debug(string.Format("updating Cookies {0} => {1}", CookieHeader, newCookieHeader));
|
||||
CookieHeader = newCookieHeader;
|
||||
if (IsConfigured)
|
||||
SaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DoFollowIfRedirect(WebClientByteResult incomingResponse, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null, bool accumulateCookies = false)
|
||||
{
|
||||
if (incomingResponse.IsRedirect)
|
||||
@@ -306,7 +325,9 @@ namespace Jackett.Indexers
|
||||
|
||||
if (cookieOverride != null)
|
||||
request.Cookies = cookieOverride;
|
||||
return await webclient.GetString(request);
|
||||
WebClientStringResult result = await webclient.GetString(request);
|
||||
UpdateCookieHeader(result.Cookies, cookieOverride);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected async Task<WebClientStringResult> RequestStringWithCookiesAndRetry(string url, string cookieOverride = null, string referer = null, Dictionary<string, string> headers = null)
|
||||
@@ -358,7 +379,9 @@ namespace Jackett.Indexers
|
||||
|
||||
if (emulateBrowser.HasValue)
|
||||
request.EmulateBrowser = emulateBrowser.Value;
|
||||
return await webclient.GetString(request);
|
||||
WebClientStringResult result = await webclient.GetString(request);
|
||||
UpdateCookieHeader(result.Cookies, cookieOverride);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected async Task<WebClientStringResult> PostDataWithCookiesAndRetry(string url, IEnumerable<KeyValuePair<string, string>> data, string cookieOverride = null, string referer = null, Dictionary<string, string> headers = null, string rawbody = null, bool? emulateBrowser = null)
|
||||
|
227
src/Jackett/Indexers/BestFriends.cs
Normal file
@@ -0,0 +1,227 @@
|
||||
using Jackett.Utils.Clients;
|
||||
using NLog;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using CsQuery;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class BestFriends : BaseIndexer, IIndexer
|
||||
{
|
||||
string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
string TakeLoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
string BrowseUrl { get { return SiteLink + "browse.php"; } }
|
||||
|
||||
new ConfigurationDataCaptchaLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataCaptchaLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public BestFriends(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "Best Friends",
|
||||
description: "A German general tracker.",
|
||||
link: "http://bf.mine.nu/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataCaptchaLogin())
|
||||
{
|
||||
AddCategoryMapping(18, TorznabCatType.TVAnime); // Anime
|
||||
AddCategoryMapping(8, TorznabCatType.PCMac); // Appz MAC
|
||||
AddCategoryMapping(9, TorznabCatType.PC); // Appz other
|
||||
AddCategoryMapping(7, TorznabCatType.PC); // Appz Windows
|
||||
AddCategoryMapping(23, TorznabCatType.TVDocumentary); // Dokumentationen
|
||||
AddCategoryMapping(32, TorznabCatType.Movies3D); // DVD 3D
|
||||
AddCategoryMapping(15, TorznabCatType.Books); // eBooks
|
||||
AddCategoryMapping(12, TorznabCatType.PCGames); // Games PC
|
||||
AddCategoryMapping(37, TorznabCatType.PCPhoneOther); // Handy_Mobile
|
||||
AddCategoryMapping(24, TorznabCatType.TVHD); // HDTV
|
||||
AddCategoryMapping(22, TorznabCatType.AudioAudiobook); // Hörbücher
|
||||
AddCategoryMapping(1, TorznabCatType.MoviesHD); // Movies 1080p/1080i
|
||||
AddCategoryMapping(31, TorznabCatType.Movies3D); // Movies 3D
|
||||
AddCategoryMapping(2, TorznabCatType.MoviesHD); // Movies 720p/720i
|
||||
AddCategoryMapping(21, TorznabCatType.MoviesBluRay); // Movies BluRay
|
||||
AddCategoryMapping(5, TorznabCatType.MoviesDVD); // Movies DVD/HDDVD
|
||||
AddCategoryMapping(6, TorznabCatType.MoviesSD); // Movies M/SVCD/Other
|
||||
AddCategoryMapping(4, TorznabCatType.MoviesSD); // Movies XVID/DIVX/h.264
|
||||
AddCategoryMapping(10, TorznabCatType.Audio); // Music
|
||||
AddCategoryMapping(25, TorznabCatType.AudioVideo); // Musikvideo
|
||||
AddCategoryMapping(29, TorznabCatType.ConsoleNDS); // Nintendo DS
|
||||
AddCategoryMapping(16, TorznabCatType.Other); // other
|
||||
AddCategoryMapping(13, TorznabCatType.ConsolePS4); // Playstation
|
||||
AddCategoryMapping(28, TorznabCatType.TVHD); // Serien HD
|
||||
AddCategoryMapping(11, TorznabCatType.TVSD); // Serien XviD
|
||||
AddCategoryMapping(33, TorznabCatType.Other); // Specials
|
||||
AddCategoryMapping(30, TorznabCatType.TVSport); // Sport
|
||||
AddCategoryMapping(19, TorznabCatType.TVOTHER); // TVRip
|
||||
AddCategoryMapping(38, TorznabCatType.TVDocumentary); // US Dokus
|
||||
AddCategoryMapping(20, TorznabCatType.MoviesForeign); // US Movies
|
||||
AddCategoryMapping(14, TorznabCatType.TVFOREIGN); // US Serien
|
||||
AddCategoryMapping(36, TorznabCatType.Other); // Wallpaper
|
||||
AddCategoryMapping(26, TorznabCatType.ConsoleWii); // Wii
|
||||
AddCategoryMapping(27, TorznabCatType.ConsoleXbox360); // Xbox 360
|
||||
AddCategoryMapping(3, TorznabCatType.XXX); // XXX
|
||||
}
|
||||
|
||||
public override async Task<ConfigurationData> GetConfigurationForSetup()
|
||||
{
|
||||
var loginPage = await RequestStringWithCookies(LoginUrl, string.Empty);
|
||||
CQ dom = loginPage.Content;
|
||||
CQ qCaptchaImg = dom.Find("td.tablea > img").First();
|
||||
|
||||
var CaptchaUrl = SiteLink + qCaptchaImg.Attr("src");
|
||||
var captchaImage = await RequestBytesWithCookies(CaptchaUrl, loginPage.Cookies);
|
||||
configData.CaptchaImage.Value = captchaImage.Content;
|
||||
configData.CaptchaCookie.Value = loginPage.Cookies;
|
||||
return configData;
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
var pairs1 = new Dictionary<string, string>
|
||||
{
|
||||
{ "proofcode", configData.CaptchaText.Value }
|
||||
};
|
||||
var cookies = configData.CaptchaCookie.Value;
|
||||
var result1 = await RequestLoginAndFollowRedirect(LoginUrl, pairs1, cookies, true, null, LoginUrl, true);
|
||||
if(result1.Content == null || !result1.Content.Contains("takelogin.php"))
|
||||
{
|
||||
CQ dom = result1.Content;
|
||||
var errorMessage = dom["#login_error"].Text().Trim();
|
||||
errorMessage = result1.Content;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
}
|
||||
|
||||
var pairs2 = new Dictionary<string, string>
|
||||
{
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value }
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(TakeLoginUrl, pairs2, result1.Cookies, true, null, LoginUrl, true);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var errorMessage = dom["#login_error"].Text().Trim();
|
||||
errorMessage = result.Content;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
TimeZoneInfo.TransitionTime startTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 3, 0, 0), 3, 5, DayOfWeek.Sunday);
|
||||
TimeZoneInfo.TransitionTime endTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 4, 0, 0), 10, 5, DayOfWeek.Sunday);
|
||||
TimeSpan delta = new TimeSpan(1, 0, 0);
|
||||
TimeZoneInfo.AdjustmentRule adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(1999, 10, 1), DateTime.MaxValue.Date, delta, startTransition, endTransition);
|
||||
TimeZoneInfo.AdjustmentRule[] adjustments = { adjustment };
|
||||
TimeZoneInfo germanyTz = TimeZoneInfo.CreateCustomTimeZone("W. Europe Standard Time", new TimeSpan(1, 0, 0), "(GMT+01:00) W. Europe Standard Time", "W. Europe Standard Time", "W. Europe DST Time", adjustments);
|
||||
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = BrowseUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
queryCollection.Add("showsearch", "1");
|
||||
queryCollection.Add("incldead", "1");
|
||||
queryCollection.Add("blah", "0");
|
||||
queryCollection.Add("orderby", "added");
|
||||
queryCollection.Add("sort", "desc");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
queryCollection.Add("search", searchString);
|
||||
}
|
||||
|
||||
foreach (var cat in MapTorznabCapsToTrackers(query))
|
||||
{
|
||||
queryCollection.Add("c" + cat, "1");
|
||||
}
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
var response = await RequestStringWithCookiesAndRetry(searchUrl, null, BrowseUrl);
|
||||
var results = response.Content;
|
||||
try
|
||||
{
|
||||
CQ dom = results;
|
||||
var rows = dom["table.tableinborder > tbody > tr:has(td.tableb)"];
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.MinimumRatio = 0.75;
|
||||
release.MinimumSeedTime = 0;
|
||||
var qRow = row.Cq();
|
||||
|
||||
var qDetailsLink = qRow.Find("a[href^=details.php?id=]").First();
|
||||
release.Title = qDetailsLink.Attr("title");
|
||||
|
||||
if (!query.MatchQueryStringAND(release.Title))
|
||||
continue;
|
||||
|
||||
var qCatLink = qRow.Find("a[href^=browse.php?cat=]").First();
|
||||
var qSeeders = qRow.Find("td:eq(7)");
|
||||
var qLeechers = qRow.Find("td:eq(8)");
|
||||
var qDateStr = qRow.Find("td:eq(4)");
|
||||
var qSize = qRow.Find("td:eq(5)");
|
||||
|
||||
var torrentId = qDetailsLink.Attr("href").Replace("&hit=1", "").Split('=')[1];
|
||||
|
||||
var catStr = qCatLink.Attr("href").Split('=')[1];
|
||||
release.Category = MapTrackerCatToNewznab(catStr);
|
||||
|
||||
release.Link = new Uri(SiteLink + "download.php?torrent="+torrentId);
|
||||
release.Comments = new Uri(SiteLink + qDetailsLink.Attr("href"));
|
||||
release.Guid = release.Link;
|
||||
|
||||
var sizeStr = qSize.Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr.Replace(",", "."));
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(qSeeders.Text());
|
||||
release.Peers = ParseUtil.CoerceInt(qLeechers.Text()) + release.Seeders;
|
||||
|
||||
var dateStr = qDateStr.Text();
|
||||
var dateGerman = DateTime.SpecifyKind(DateTime.ParseExact(dateStr, "dd.MM.yyyyHH:mm:ss", CultureInfo.InvariantCulture), DateTimeKind.Unspecified);
|
||||
DateTime pubDateUtc = TimeZoneInfo.ConvertTimeToUtc(dateGerman, germanyTz);
|
||||
release.PublishDate = pubDateUtc;
|
||||
|
||||
var files = qRow.Find("td:nth-child(3)").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(7)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (qRow.Find("font[color=\"red\"]:contains(OnlyUp)").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -30,7 +30,7 @@ namespace Jackett.Indexers
|
||||
public BeyondHD(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps)
|
||||
: base(name: "BeyondHD",
|
||||
description: "Without BeyondHD, your HDTV is just a TV",
|
||||
link: "https://beyondhd.me/",
|
||||
link: "https://beyond-hd.me/",
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: w,
|
||||
@@ -79,10 +79,11 @@ namespace Jackett.Indexers
|
||||
public override async Task<ConfigurationData> GetConfigurationForSetup()
|
||||
{
|
||||
var loginPage = await RequestStringWithCookies(LoginUrl, string.Empty);
|
||||
string recaptchaSiteKey = new Regex(@"loginwidget', \{[\s]{4,30}'sitekey' : '([0-9A-Za-z]{5,60})',[\s]{4,30}'theme'").Match(loginPage.Content).Groups[1].ToString().Trim();
|
||||
var result = new ConfigurationDataRecaptchaLogin();
|
||||
string recaptchaSiteKey = new Regex(@"loginwidget', \{[\s]{4,30}'sitekey' : '([0-9A-Za-z-]{5,60})',[\s]{4,30}'theme'").Match(loginPage.Content).Groups[1].ToString().Trim();
|
||||
var result = this.configData;
|
||||
result.CookieHeader.Value = loginPage.Cookies;
|
||||
result.Captcha.SiteKey = recaptchaSiteKey;
|
||||
result.Captcha.Version = "2";
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -187,6 +188,15 @@ namespace Jackett.Indexers
|
||||
release.Seeders = ParseUtil.CoerceInt(row.ChildElements.ElementAt(9).Cq().Text());
|
||||
release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(10).Cq().Text()) + release.Seeders;
|
||||
|
||||
var files = qRow.Find("td:nth-child(6)").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(9) > a").Get(0).FirstChild.ToString();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
release.DownloadVolumeFactor = 0; // ratioless
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
|
||||
}
|
||||
|
199
src/Jackett/Indexers/BitCityReloaded.cs
Normal file
@@ -0,0 +1,199 @@
|
||||
using Jackett.Utils.Clients;
|
||||
using NLog;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using CsQuery;
|
||||
using System.Web;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class BitCityReloaded : BaseIndexer, IIndexer
|
||||
{
|
||||
string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
string BrowseUrl { get { return SiteLink + "uebersicht.php"; } }
|
||||
TimeZoneInfo germanyTz = TimeZoneInfo.CreateCustomTimeZone("W. Europe Standard Time", new TimeSpan(1, 0, 0), "W. Europe Standard Time", "W. Europe Standard Time");
|
||||
|
||||
new ConfigurationDataBasicLoginWithRSSAndDisplay configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLoginWithRSSAndDisplay)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public BitCityReloaded(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "Bit-City Reloaded",
|
||||
description: "A German general tracker.",
|
||||
link: "https://bc-reloaded.net/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLoginWithRSSAndDisplay())
|
||||
{
|
||||
this.configData.DisplayText.Value = "Only the results from the first search result page are shown, adjust your profile settings to show a reasonable amount (it looks like there's no maximum).";
|
||||
this.configData.DisplayText.Name = "Notice";
|
||||
|
||||
AddCategoryMapping(1, TorznabCatType.Other); // Anderes
|
||||
AddCategoryMapping(2, TorznabCatType.TVAnime); // Anime
|
||||
AddCategoryMapping(34, TorznabCatType.PC); // Appz/Linux
|
||||
AddCategoryMapping(35, TorznabCatType.PCMac); // Appz/Mac
|
||||
AddCategoryMapping(36, TorznabCatType.PC); // Appz/Other
|
||||
AddCategoryMapping(20, TorznabCatType.PC); // Appz/Win
|
||||
AddCategoryMapping(3, TorznabCatType.TVDocumentary); // Doku/Alle Formate
|
||||
AddCategoryMapping(4, TorznabCatType.Books); // EBooks
|
||||
AddCategoryMapping(12, TorznabCatType.ConsolePS4); // Games PS / PSX
|
||||
AddCategoryMapping(11, TorznabCatType.ConsoleNDS); // Games/Nintendo DS
|
||||
AddCategoryMapping(10, TorznabCatType.PCGames); // Games/PC
|
||||
AddCategoryMapping(13, TorznabCatType.ConsoleWii); // Games/Wii
|
||||
AddCategoryMapping(14, TorznabCatType.ConsoleXbox); // Games/Xbox & 360
|
||||
AddCategoryMapping(15, TorznabCatType.PCPhoneOther); // Handy & PDA
|
||||
AddCategoryMapping(16, TorznabCatType.AudioAudiobook); // Hörspiel/Hörbuch
|
||||
AddCategoryMapping(30, TorznabCatType.Other); // International
|
||||
AddCategoryMapping(17, TorznabCatType.Other); // MegaPack
|
||||
AddCategoryMapping(43, TorznabCatType.Movies3D); // Movie/3D
|
||||
AddCategoryMapping(5, TorznabCatType.MoviesDVD); // Movie/DVD/R
|
||||
AddCategoryMapping(6, TorznabCatType.MoviesHD); // Movie/HD 1080p
|
||||
AddCategoryMapping(7, TorznabCatType.MoviesHD); // Movie/HD 720p
|
||||
AddCategoryMapping(32, TorznabCatType.MoviesOther); // Movie/TVRip
|
||||
AddCategoryMapping(9, TorznabCatType.MoviesOther); // Movie/XviD,DivX,h264
|
||||
AddCategoryMapping(26, TorznabCatType.XXX); // Movie/XXX
|
||||
AddCategoryMapping(41, TorznabCatType.XXXOther); // Movie/XXX/Other
|
||||
AddCategoryMapping(42, TorznabCatType.XXXPacks); // Movie/XXX/Pack
|
||||
AddCategoryMapping(45, TorznabCatType.MoviesHD); // Movies/4K
|
||||
AddCategoryMapping(33, TorznabCatType.MoviesBluRay); // Movies/BluRay
|
||||
AddCategoryMapping(18, TorznabCatType.Audio); // Musik
|
||||
AddCategoryMapping(19, TorznabCatType.AudioVideo); // Musik Videos
|
||||
AddCategoryMapping(44, TorznabCatType.TVOTHER); // Serie/DVD/R
|
||||
AddCategoryMapping(22, TorznabCatType.TVHD); // Serie/HDTV
|
||||
AddCategoryMapping(38, TorznabCatType.TV); // Serie/Pack
|
||||
AddCategoryMapping(23, TorznabCatType.TVOTHER); // Serie/XviD,DivX,h264
|
||||
AddCategoryMapping(25, TorznabCatType.TVSport); // Sport
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
var pairs = new Dictionary<string, string>
|
||||
{
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value }
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, LoginUrl);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var errorMessage = dom["#login_error"].Text().Trim();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = BrowseUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
queryCollection.Add("showsearch", "0");
|
||||
queryCollection.Add("incldead", "1");
|
||||
queryCollection.Add("blah", "0");
|
||||
queryCollection.Add("team", "0");
|
||||
queryCollection.Add("orderby", "added");
|
||||
queryCollection.Add("sort", "desc");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
queryCollection.Add("search", searchString);
|
||||
}
|
||||
|
||||
foreach (var cat in MapTorznabCapsToTrackers(query))
|
||||
{
|
||||
queryCollection.Add("c" + cat, "1");
|
||||
}
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
var response = await RequestStringWithCookiesAndRetry(searchUrl, null, BrowseUrl);
|
||||
var results = response.Content;
|
||||
try
|
||||
{
|
||||
CQ dom = results;
|
||||
var rows = dom["table.tableinborder[cellpadding=0] > tbody > tr"];
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.MinimumRatio = 0.7;
|
||||
release.MinimumSeedTime = 48 * 60 * 60;
|
||||
var qRow = row.Cq();
|
||||
var flagImgs = qRow.Find("table tbody tr: eq(0) td > img");
|
||||
List<string> flags = new List<string>();
|
||||
flagImgs.Each(flagImg => {
|
||||
flags.Add(flagImg.GetAttribute("src").Replace("pic/torrent_", "").Replace(".gif", "").ToUpper());
|
||||
});
|
||||
|
||||
var titleLink = qRow.Find("table tbody tr:eq(0) td a:has(b)").First();
|
||||
var DLLink = qRow.Find("td.tableb > a:has(img[title=\"Torrent herunterladen\"])").First();
|
||||
release.Comments = new Uri(SiteLink + titleLink.Attr("href").Replace("&hit=1", ""));
|
||||
release.Link = new Uri(SiteLink + DLLink.Attr("href"));
|
||||
release.Title = titleLink.Text().Trim();
|
||||
|
||||
if (!query.MatchQueryStringAND(release.Title))
|
||||
continue;
|
||||
|
||||
release.Description = String.Join(", ", flags);
|
||||
release.Guid = release.Link;
|
||||
|
||||
var dateStr = qRow.Find("table tbody tr:eq(1) td:eq(4)").Html().Replace(" ", " ").Trim();
|
||||
var dateGerman = DateTime.SpecifyKind(DateTime.ParseExact(dateStr, "dd.MM.yyyy HH:mm:ss", CultureInfo.InvariantCulture), DateTimeKind.Unspecified);
|
||||
DateTime pubDateUtc = TimeZoneInfo.ConvertTimeToUtc(dateGerman, germanyTz);
|
||||
release.PublishDate = pubDateUtc.ToLocalTime();
|
||||
|
||||
var sizeStr = qRow.Find("table tbody tr:eq(1) td b").First().Text().Trim();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr.Replace(",", "."));
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(qRow.Find("table tbody tr:eq(1) td:eq(1) b:eq(0) font").Text().Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(qRow.Find("table tbody tr:eq(1) td:eq(1) b:eq(1) font").Text().Trim()) + release.Seeders;
|
||||
|
||||
var catId = qRow.Find("td:eq(0) a").First().Attr("href").Split('=')[1];
|
||||
release.Category = MapTrackerCatToNewznab(catId);
|
||||
|
||||
var cat = qRow.Find("td:eq(0) a").First().Attr("href").Substring(1);
|
||||
release.Category = MapTrackerCatToNewznab(cat);
|
||||
|
||||
var files = qRow.Find("td:has(a[href*=\"&filelist=1\"])> b:nth-child(2)").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = qRow.Find("td:has(a[href*=\"&tosnatchers=1\"])> b:nth-child(1)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (qRow.Find("img[src=\"pic/torrent_ou.gif\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@ namespace Jackett.Indexers
|
||||
{
|
||||
private string LoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "torrents.php?"; } }
|
||||
private string DownloadUrl { get { return SiteLink + "download.php?/{0}/dl.torrent"; } }
|
||||
private string DownloadUrl { get { return SiteLink + "download.php?id={0}"; } }
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
|
@@ -129,6 +129,15 @@ namespace Jackett.Indexers
|
||||
//if (!release.Title.ToLower().Contains(title.ToLower()))
|
||||
// continue;
|
||||
|
||||
var files = row.Cq().Find("td:nth-child(4)").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = row.Cq().Find("td:nth-child(8)").Get(0).FirstChild.ToString();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
release.DownloadVolumeFactor = 1;
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
@@ -25,12 +25,12 @@ namespace Jackett.Indexers
|
||||
private string UseLink { get { return (this.configData.AlternateLink.Value != null && this.configData.AlternateLink.Value != "" ? this.configData.AlternateLink.Value : SiteLink); } }
|
||||
private string BrowseUrl { get { return UseLink + "browse.php"; } }
|
||||
private string LoginUrl { get { return UseLink + "takelogin.php"; } }
|
||||
private string LoginReferer { get { return UseLink + "login.php"; } }
|
||||
private List<String> KnownURLs = new List<String>{ "https://www.bitsoup.me/","https://www.bitsoup.org/"};
|
||||
private string LoginReferer { get { return UseLink + "login.php"; } }
|
||||
private List<String> KnownURLs = new List<String> { "https://www.bitsoup.me/", "https://www.bitsoup.org/" };
|
||||
|
||||
new NxtGnConfigurationData configData
|
||||
new ConfigurationDataBasicLoginWithAlternateLink configData
|
||||
{
|
||||
get { return (NxtGnConfigurationData)base.configData; }
|
||||
get { return (ConfigurationDataBasicLoginWithAlternateLink)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
@@ -43,11 +43,11 @@ namespace Jackett.Indexers
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new NxtGnConfigurationData())
|
||||
configData: new ConfigurationDataBasicLoginWithAlternateLink())
|
||||
{
|
||||
this.configData.DisplayText.Value = this.DisplayName + " has multiple URLs. The default (" + this.SiteLink + ") can be changed by entering a new value in the box below.";
|
||||
this.configData.DisplayText.Value += "The following are some known URLs for " + this.DisplayName;
|
||||
this.configData.DisplayText.Value += "<ul><li>" + String.Join("</li><li>", this.KnownURLs.ToArray()) + "</li></ul>";
|
||||
this.configData.Instructions.Value = this.DisplayName + " has multiple URLs. The default (" + this.SiteLink + ") can be changed by entering a new value in the box below.";
|
||||
this.configData.Instructions.Value += "The following are some known URLs for " + this.DisplayName;
|
||||
this.configData.Instructions.Value += "<ul><li>" + String.Join("</li><li>", this.KnownURLs.ToArray()) + "</li></ul>";
|
||||
|
||||
//AddCategoryMapping("624", TorznabCatType.Console);
|
||||
//AddCategoryMapping("307", TorznabCatType.ConsoleNDS);
|
||||
@@ -158,12 +158,12 @@ namespace Jackett.Indexers
|
||||
{
|
||||
configData.AlternateLink.Value = null;
|
||||
throw new Exception("AlternateLink must be a valid url.");
|
||||
}
|
||||
}
|
||||
}
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
|
||||
|
||||
};
|
||||
|
||||
var loginPage = await RequestStringWithCookies(UseLink, string.Empty);
|
||||
@@ -188,11 +188,12 @@ namespace Jackett.Indexers
|
||||
var queryCollection = new NameValueCollection();
|
||||
|
||||
|
||||
queryCollection.Add("search", string.IsNullOrWhiteSpace(searchString)? "" : searchString);
|
||||
queryCollection.Add("search", string.IsNullOrWhiteSpace(searchString) ? "" : searchString);
|
||||
if (trackerCats.Count > 1)
|
||||
{
|
||||
for (var ct = 0; ct < trackerCats.Count; ct++) queryCollection.Add("cat" + (ct+1), trackerCats.ElementAt(ct));
|
||||
} else
|
||||
for (var ct = 0; ct < trackerCats.Count; ct++) queryCollection.Add("cat" + (ct + 1), trackerCats.ElementAt(ct));
|
||||
}
|
||||
else
|
||||
{
|
||||
queryCollection.Add("cat", (trackerCats.Count == 1 ? trackerCats.ElementAt(0) : "0"));
|
||||
}
|
||||
@@ -242,23 +243,5 @@ namespace Jackett.Indexers
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public class NxtGnConfigurationData : ConfigurationData
|
||||
{
|
||||
public StringItem Username { get; private set; }
|
||||
public StringItem Password { get; private set; }
|
||||
public DisplayItem DisplayText { get; private set; }
|
||||
public StringItem AlternateLink { get; set; }
|
||||
|
||||
|
||||
public NxtGnConfigurationData()
|
||||
{
|
||||
Username = new StringItem { Name = "Username" };
|
||||
Password = new StringItem { Name = "Password" };
|
||||
DisplayText = new DisplayItem("") { Name = "" };
|
||||
AlternateLink = new StringItem { Name = "AlternateLinks" };
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -78,6 +78,7 @@ namespace Jackett.Indexers
|
||||
AddCategoryMapping("25", TorznabCatType.MoviesOther);
|
||||
AddCategoryMapping("21", TorznabCatType.MoviesOther);
|
||||
AddCategoryMapping("20", TorznabCatType.MoviesDVD);
|
||||
AddCategoryMapping("26", TorznabCatType.MoviesWEBDL);
|
||||
AddCategoryMapping("9", TorznabCatType.TVAnime);
|
||||
AddCategoryMapping("34", TorznabCatType.Other);
|
||||
AddCategoryMapping("35", TorznabCatType.Audio);
|
||||
|
636
src/Jackett/Indexers/CardigannIndexer.cs
Normal file
@@ -0,0 +1,636 @@
|
||||
using Jackett.Utils.Clients;
|
||||
using NLog;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
using System.Text;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
using static Jackett.Models.IndexerConfig.ConfigurationData;
|
||||
using AngleSharp.Parser.Html;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class CardigannIndexer : BaseIndexer, IIndexer
|
||||
{
|
||||
protected IndexerDefinition Definition;
|
||||
public new string ID { get { return (Definition != null ? Definition.Site : GetIndexerID(GetType())); } }
|
||||
|
||||
new ConfigurationData configData
|
||||
{
|
||||
get { return (ConfigurationData)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
// Cardigann yaml classes
|
||||
public class IndexerDefinition {
|
||||
public string Site { get; set; }
|
||||
public List<settingsField> Settings { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Language { get; set; }
|
||||
public List<string> Links { get; set; }
|
||||
public capabilitiesBlock Caps { get; set; }
|
||||
public loginBlock Login { get; set; }
|
||||
public ratioBlock Ratio { get; set; }
|
||||
public searchBlock Search { get; set; }
|
||||
// IndexerDefinitionStats not needed/implemented
|
||||
}
|
||||
public class settingsField
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string Label { get; set; }
|
||||
}
|
||||
|
||||
public class capabilitiesBlock
|
||||
{
|
||||
public Dictionary<string, string> Categories { get; set; }
|
||||
public Dictionary<string, List<string>> Modes { get; set; }
|
||||
}
|
||||
|
||||
public class loginBlock
|
||||
{
|
||||
public string Path { get; set; }
|
||||
public string Method { get; set; }
|
||||
public string Form { get; set; }
|
||||
public Dictionary<string, string> Inputs { get; set; }
|
||||
public List<errorBlock> Error { get; set; }
|
||||
public pageTestBlock Test { get; set; }
|
||||
}
|
||||
|
||||
public class errorBlock
|
||||
{
|
||||
public string Path { get; set; }
|
||||
public string Selector { get; set; }
|
||||
public selectorBlock Message { get; set; }
|
||||
}
|
||||
|
||||
public class selectorBlock
|
||||
{
|
||||
public string Selector { get; set; }
|
||||
public string Text { get; set; }
|
||||
public string Attribute { get; set; }
|
||||
public string Remove { get; set; }
|
||||
public List<filterBlock> Filters { get; set; }
|
||||
public Dictionary<string, string> Case { get; set; }
|
||||
}
|
||||
|
||||
public class filterBlock
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public dynamic Args { get; set; }
|
||||
}
|
||||
|
||||
public class pageTestBlock
|
||||
{
|
||||
public string Path { get; set; }
|
||||
public string Selector { get; set; }
|
||||
}
|
||||
|
||||
public class ratioBlock : selectorBlock
|
||||
{
|
||||
public string Path { get; set; }
|
||||
}
|
||||
|
||||
public class searchBlock
|
||||
{
|
||||
public string Path { get; set; }
|
||||
public Dictionary<string, string> Inputs { get; set; }
|
||||
public rowsBlock Rows { get; set; }
|
||||
public Dictionary<string, selectorBlock> Fields { get; set; }
|
||||
}
|
||||
|
||||
public class rowsBlock : selectorBlock
|
||||
{
|
||||
public int After { get; set; }
|
||||
//public string Remove { get; set; } // already inherited
|
||||
public string Dateheaders { get; set; }
|
||||
}
|
||||
|
||||
public CardigannIndexer(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps)
|
||||
{
|
||||
}
|
||||
|
||||
public CardigannIndexer(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps, string DefinitionString)
|
||||
: base(manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps)
|
||||
{
|
||||
Init(DefinitionString);
|
||||
}
|
||||
|
||||
protected void Init(string DefinitionString)
|
||||
{
|
||||
var deserializer = new DeserializerBuilder()
|
||||
.WithNamingConvention(new CamelCaseNamingConvention())
|
||||
.IgnoreUnmatchedProperties()
|
||||
.Build();
|
||||
Definition = deserializer.Deserialize<IndexerDefinition>(DefinitionString);
|
||||
|
||||
// Add default data if necessary
|
||||
if (Definition.Settings == null)
|
||||
Definition.Settings = new List<settingsField>();
|
||||
|
||||
if (Definition.Settings.Count == 0)
|
||||
{
|
||||
Definition.Settings.Add(new settingsField { Name = "username", Label = "Username", Type = "text" });
|
||||
Definition.Settings.Add(new settingsField { Name = "password", Label = "Password", Type = "password" });
|
||||
}
|
||||
|
||||
// init missing mandatory attributes
|
||||
DisplayName = Definition.Name;
|
||||
DisplayDescription = Definition.Description;
|
||||
SiteLink = Definition.Links[0]; // TODO: implement alternative links
|
||||
if (!SiteLink.EndsWith("/"))
|
||||
SiteLink += "/";
|
||||
TorznabCaps = TorznabUtil.CreateDefaultTorznabTVCaps(); // TODO implement caps
|
||||
|
||||
// init config Data
|
||||
configData = new ConfigurationData();
|
||||
foreach (var Setting in Definition.Settings)
|
||||
{
|
||||
configData.AddDynamic(Setting.Name, new StringItem { Name = Setting.Label });
|
||||
}
|
||||
|
||||
foreach (var Category in Definition.Caps.Categories)
|
||||
{
|
||||
var cat = TorznabCatType.GetCatByName(Category.Value);
|
||||
if (cat == null)
|
||||
{
|
||||
logger.Error(string.Format("CardigannIndexer ({0}): Can't find a category for {1}", ID, Category.Value));
|
||||
continue;
|
||||
}
|
||||
AddCategoryMapping(Category.Key, TorznabCatType.GetCatByName(Category.Value));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected Dictionary<string, object> getTemplateVariablesFromConfigData()
|
||||
{
|
||||
Dictionary<string, object> variables = new Dictionary<string, object>();
|
||||
|
||||
foreach (settingsField Setting in Definition.Settings)
|
||||
{
|
||||
variables[".Config."+Setting.Name] = ((StringItem)configData.GetDynamic(Setting.Name)).Value;
|
||||
}
|
||||
return variables;
|
||||
}
|
||||
|
||||
// A very bad implementation of the golang template/text templating engine.
|
||||
// But it should work for most basic constucts used by Cardigann definitions.
|
||||
protected string applyGoTemplateText(string template, Dictionary<string, object> variables = null)
|
||||
{
|
||||
if (variables == null)
|
||||
{
|
||||
variables = getTemplateVariablesFromConfigData();
|
||||
}
|
||||
|
||||
// handle if ... else ... expression
|
||||
Regex IfElseRegex = new Regex(@"{{if\s*(.+?)\s*}}(.*?){{\s*else\s*}}(.*?){{\s*end\s*}}");
|
||||
var IfElseRegexMatches = IfElseRegex.Match(template);
|
||||
|
||||
while (IfElseRegexMatches.Success)
|
||||
{
|
||||
string conditionResult = null;
|
||||
|
||||
string all = IfElseRegexMatches.Groups[0].Value;
|
||||
string condition = IfElseRegexMatches.Groups[1].Value;
|
||||
string onTrue = IfElseRegexMatches.Groups[2].Value;
|
||||
string onFalse = IfElseRegexMatches.Groups[3].Value;
|
||||
|
||||
if (condition.StartsWith("."))
|
||||
{
|
||||
string value = (string)variables[condition];
|
||||
if (!string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
conditionResult = onTrue;
|
||||
}
|
||||
else
|
||||
{
|
||||
conditionResult = onFalse;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException("CardigannIndexer: Condition operation '" + condition + "' not implemented");
|
||||
}
|
||||
template = template.Replace(all, conditionResult);
|
||||
IfElseRegexMatches = IfElseRegexMatches.NextMatch();
|
||||
}
|
||||
|
||||
// handle range expression
|
||||
Regex RangeRegex = new Regex(@"{{\s*range\s*(.+?)\s*}}(.*?){{\.}}(.*?){{end}}");
|
||||
var RangeRegexMatches = RangeRegex.Match(template);
|
||||
|
||||
while (RangeRegexMatches.Success)
|
||||
{
|
||||
string expanded = string.Empty;
|
||||
|
||||
string all = RangeRegexMatches.Groups[0].Value;
|
||||
string variable = RangeRegexMatches.Groups[1].Value;
|
||||
string prefix = RangeRegexMatches.Groups[2].Value;
|
||||
string postfix = RangeRegexMatches.Groups[3].Value;
|
||||
|
||||
foreach (string value in (List<string>)variables[variable])
|
||||
{
|
||||
expanded += prefix + value + postfix;
|
||||
}
|
||||
template = template.Replace(all, expanded);
|
||||
RangeRegexMatches = RangeRegexMatches.NextMatch();
|
||||
}
|
||||
|
||||
// handle simple variables
|
||||
Regex VariablesRegEx = new Regex(@"{{\s*(\..+?)\s*}}");
|
||||
var VariablesRegExMatches = VariablesRegEx.Match(template);
|
||||
|
||||
while (VariablesRegExMatches.Success)
|
||||
{
|
||||
string expanded = string.Empty;
|
||||
|
||||
string all = VariablesRegExMatches.Groups[0].Value;
|
||||
string variable = VariablesRegExMatches.Groups[1].Value;
|
||||
|
||||
string value = (string)variables[variable];
|
||||
template = template.Replace(all, value);
|
||||
VariablesRegExMatches = VariablesRegExMatches.NextMatch();
|
||||
}
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
protected async Task<bool> DoLogin()
|
||||
{
|
||||
var Login = Definition.Login;
|
||||
|
||||
if (Login == null)
|
||||
return false;
|
||||
|
||||
if (Login.Method == "post")
|
||||
{
|
||||
var pairs = new Dictionary<string, string>();
|
||||
foreach (var Input in Definition.Login.Inputs)
|
||||
{
|
||||
var value = applyGoTemplateText(Input.Value);
|
||||
pairs.Add(Input.Key, value);
|
||||
}
|
||||
foreach (var x in pairs)
|
||||
{
|
||||
logger.Error(x.Key + ": " + x.Value);
|
||||
}
|
||||
var LoginUrl = SiteLink + Login.Path;
|
||||
configData.CookieHeader.Value = null;
|
||||
var loginResult = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, SiteLink, true);
|
||||
configData.CookieHeader.Value = loginResult.Cookies;
|
||||
|
||||
if (Login.Error != null)
|
||||
{
|
||||
var loginResultParser = new HtmlParser();
|
||||
var loginResultDocument = loginResultParser.Parse(loginResult.Content);
|
||||
foreach (errorBlock error in Login.Error)
|
||||
{
|
||||
var selection = loginResultDocument.QuerySelector(error.Selector);
|
||||
if (selection != null)
|
||||
{
|
||||
string errorMessage = selection.TextContent;
|
||||
if (error.Message != null)
|
||||
{
|
||||
var errorSubMessage = loginResultDocument.QuerySelector(error.Message.Selector);
|
||||
errorMessage = errorSubMessage.TextContent;
|
||||
}
|
||||
throw new ExceptionWithConfigData(string.Format("Login failed: {0}", errorMessage.Trim()), configData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Login.Method == "cookie")
|
||||
{
|
||||
configData.CookieHeader.Value = ((StringItem)configData.GetDynamic("cookie")).Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException("Login method " + Definition.Login.Method + " not implemented");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected async Task<bool> TestLogin()
|
||||
{
|
||||
var Login = Definition.Login;
|
||||
|
||||
if (Login == null || Login.Test == null)
|
||||
return false;
|
||||
|
||||
// test if login was successful
|
||||
var LoginTestUrl = SiteLink + Login.Test.Path;
|
||||
var testResult = await RequestStringWithCookies(LoginTestUrl);
|
||||
|
||||
if (testResult.IsRedirect)
|
||||
{
|
||||
throw new ExceptionWithConfigData("Login Failed, got redirected", configData);
|
||||
}
|
||||
|
||||
if (Login.Test.Selector != null)
|
||||
{
|
||||
var testResultParser = new HtmlParser();
|
||||
var testResultDocument = testResultParser.Parse(testResult.Content);
|
||||
var selection = testResultDocument.QuerySelectorAll(Login.Test.Selector);
|
||||
if (selection.Length == 0)
|
||||
{
|
||||
throw new ExceptionWithConfigData(string.Format("Login failed: Selector \"{0}\" didn't match", Login.Test.Selector), configData);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
await DoLogin();
|
||||
await TestLogin();
|
||||
|
||||
SaveConfig();
|
||||
IsConfigured = true;
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
|
||||
protected string applyFilters(string Data, List<filterBlock> Filters)
|
||||
{
|
||||
if (Filters == null)
|
||||
return Data;
|
||||
|
||||
foreach(filterBlock Filter in Filters)
|
||||
{
|
||||
switch (Filter.Name)
|
||||
{
|
||||
case "querystring":
|
||||
var param = (string)Filter.Args;
|
||||
var qsStr = Data.Split(new char[] { '?' }, 2)[1];
|
||||
qsStr = Data.Split(new char[] { '#' }, 2)[0];
|
||||
var qs = HttpUtility.ParseQueryString(qsStr);
|
||||
Data = qs.Get(param);
|
||||
break;
|
||||
case "timeparse":
|
||||
case "dateparse":
|
||||
throw new NotImplementedException("Filter " + Filter.Name + " not implemented");
|
||||
/*
|
||||
TODO: implement golang time format conversion, see http://fuckinggodateformat.com/
|
||||
if args == nil {
|
||||
return filterDateParse(nil, value)
|
||||
}
|
||||
if layout, ok := args.(string); ok {
|
||||
return filterDateParse([]string{layout}, value)
|
||||
}
|
||||
return "", fmt.Errorf("Filter argument type %T was invalid", args)
|
||||
*/
|
||||
break;
|
||||
case "regexp":
|
||||
var pattern = (string)Filter.Args;
|
||||
var Regexp = new Regex(pattern);
|
||||
var Match = Regexp.Match(Data);
|
||||
Data = Match.Groups[1].Value;
|
||||
break;
|
||||
case "split":
|
||||
var sep = (string)Filter.Args[0];
|
||||
var pos = (string)Filter.Args[1];
|
||||
var posInt = int.Parse(pos);
|
||||
var strParts = Data.Split(sep[0]);
|
||||
if (posInt < 0)
|
||||
{
|
||||
posInt += strParts.Length;
|
||||
}
|
||||
Data = strParts[posInt];
|
||||
break;
|
||||
case "replace":
|
||||
var from = (string)Filter.Args[0];
|
||||
var to = (string)Filter.Args[1];
|
||||
Data = Data.Replace(from, to);
|
||||
break;
|
||||
case "trim":
|
||||
var cutset = (string)Filter.Args;
|
||||
Data = Data.Trim(cutset[0]);
|
||||
break;
|
||||
case "append":
|
||||
var str = (string)Filter.Args;
|
||||
Data += str;
|
||||
break;
|
||||
case "timeago":
|
||||
case "fuzzytime":
|
||||
case "reltime":
|
||||
var timestr = (string)Filter.Args;
|
||||
Data = DateTimeUtil.FromUnknown(timestr).ToString(DateTimeUtil.RFC1123ZPattern);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Data;
|
||||
}
|
||||
|
||||
protected string handleSelector(selectorBlock Selector, AngleSharp.Dom.IElement Dom)
|
||||
{
|
||||
if (Selector.Text != null)
|
||||
{
|
||||
return applyFilters(Selector.Text, Selector.Filters);
|
||||
}
|
||||
string value = null;
|
||||
if (Selector.Selector != null)
|
||||
{
|
||||
AngleSharp.Dom.IElement selection = Dom.QuerySelector(Selector.Selector);
|
||||
if (selection == null)
|
||||
{
|
||||
throw new Exception(string.Format("Selector \"{0}\" didn't match {1}", Selector.Selector, Dom.OuterHtml));
|
||||
}
|
||||
if (Selector.Remove != null)
|
||||
{
|
||||
foreach(var i in selection.QuerySelectorAll(Selector.Remove))
|
||||
{
|
||||
i.Remove();
|
||||
}
|
||||
}
|
||||
if (Selector.Attribute != null)
|
||||
{
|
||||
value = selection.GetAttribute(Selector.Attribute);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = selection.TextContent;
|
||||
}
|
||||
|
||||
}
|
||||
return applyFilters(value, Selector.Filters); ;
|
||||
}
|
||||
|
||||
protected Uri resolvePath(string path)
|
||||
{
|
||||
return new Uri(SiteLink + path);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
searchBlock Search = Definition.Search;
|
||||
|
||||
// init template context
|
||||
var variables = getTemplateVariablesFromConfigData();
|
||||
|
||||
variables[".Query.Type"] = query.QueryType;
|
||||
variables[".Query.Q"] = query.SearchTerm;
|
||||
variables[".Query.Series"] = null;
|
||||
variables[".Query.Ep"] = query.Episode;
|
||||
variables[".Query.Season"] = query.Season;
|
||||
variables[".Query.Movie"] = null;
|
||||
variables[".Query.Year"] = null;
|
||||
variables[".Query.Limit"] = query.Limit;
|
||||
variables[".Query.Offset"] = query.Offset;
|
||||
variables[".Query.Extended"] = query.Extended;
|
||||
variables[".Query.Categories"] = query.Categories;
|
||||
variables[".Query.APIKey"] = query.ApiKey;
|
||||
variables[".Query.TVDBID"] = null;
|
||||
variables[".Query.TVRageID"] = query.RageID;
|
||||
variables[".Query.IMDBID"] = query.ImdbID;
|
||||
variables[".Query.TVMazeID"] = null;
|
||||
variables[".Query.TraktID"] = null;
|
||||
|
||||
variables[".Query.Episode"] = query.GetEpisodeSearchString();
|
||||
variables[".Categories"] = MapTorznabCapsToTrackers(query);
|
||||
|
||||
var KeywordTokens = new List<string>();
|
||||
var KeywordTokenKeys = new List<string> { "Q", "Series", "Movie", "Year" };
|
||||
foreach (var key in KeywordTokenKeys)
|
||||
{
|
||||
var Value = (string)variables[".Query." + key];
|
||||
if (!string.IsNullOrWhiteSpace(Value))
|
||||
KeywordTokens.Add(Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace((string)variables[".Query.Episode"]))
|
||||
KeywordTokens.Add((string)variables[".Query.Episode"]);
|
||||
variables[".Query.Keywords"] = string.Join(" ", KeywordTokens);
|
||||
variables[".Keywords"] = variables[".Query.Keywords"];
|
||||
|
||||
// build search URL
|
||||
var searchUrl = SiteLink + applyGoTemplateText(Search.Path, variables) + "?";
|
||||
var queryCollection = new NameValueCollection();
|
||||
if (Search.Inputs != null)
|
||||
{
|
||||
foreach (var Input in Search.Inputs)
|
||||
{
|
||||
var value = applyGoTemplateText(Input.Value, variables);
|
||||
if (Input.Key == "$raw")
|
||||
searchUrl += value;
|
||||
else
|
||||
queryCollection.Add(Input.Key, value);
|
||||
}
|
||||
}
|
||||
searchUrl += "&" + queryCollection.GetQueryString();
|
||||
|
||||
// send HTTP request
|
||||
var response = await RequestBytesWithCookies(searchUrl);
|
||||
var results = Encoding.GetEncoding("iso-8859-1").GetString(response.Content);
|
||||
try
|
||||
{
|
||||
var SearchResultParser = new HtmlParser();
|
||||
var SearchResultDocument = SearchResultParser.Parse(results);
|
||||
|
||||
var Rows = SearchResultDocument.QuerySelectorAll(Search.Rows.Selector);
|
||||
foreach (var Row in Rows)
|
||||
{
|
||||
try
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 48 * 60 * 60;
|
||||
|
||||
// Parse fields
|
||||
foreach (var Field in Search.Fields)
|
||||
{
|
||||
string value = handleSelector(Field.Value, Row);
|
||||
try
|
||||
{
|
||||
switch (Field.Key)
|
||||
{
|
||||
case "download":
|
||||
release.Link = resolvePath(value);
|
||||
break;
|
||||
case "details":
|
||||
var url = resolvePath(value);
|
||||
release.Guid = url;
|
||||
if (release.Comments == null)
|
||||
release.Comments = url;
|
||||
break;
|
||||
case "comments":
|
||||
release.Comments = resolvePath(value);
|
||||
break;
|
||||
case "title":
|
||||
release.Title = value;
|
||||
break;
|
||||
case "description":
|
||||
release.Description = value;
|
||||
break;
|
||||
case "category":
|
||||
release.Category = MapTrackerCatToNewznab(value);
|
||||
break;
|
||||
case "size":
|
||||
release.Size = ReleaseInfo.GetBytes(value);
|
||||
break;
|
||||
case "leechers":
|
||||
if (release.Peers == null)
|
||||
release.Peers = ParseUtil.CoerceInt(value);
|
||||
else
|
||||
release.Peers += ParseUtil.CoerceInt(value);
|
||||
break;
|
||||
case "seeders":
|
||||
release.Seeders = ParseUtil.CoerceInt(value);
|
||||
if (release.Peers == null)
|
||||
release.Peers = release.Seeders;
|
||||
else
|
||||
release.Peers += release.Seeders;
|
||||
|
||||
break;
|
||||
case "date":
|
||||
release.PublishDate = DateTimeUtil.FromUnknown(value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception(string.Format("Error while parsing field={0}, selector={1}, value={2}: {3}", Field.Key, Field.Value.Selector, value, ex.Message));
|
||||
}
|
||||
}
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(string.Format("CardigannIndexer ({0}): Error while parsing row '{1}': {2}", ID, Row.OuterHtml, ex.Message));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -18,12 +18,12 @@ using Jackett.Models.IndexerConfig;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class EuTorrents : AvistazTracker, IIndexer
|
||||
public class CinemaZ : AvistazTracker, IIndexer
|
||||
{
|
||||
public EuTorrents(IIndexerManagerService indexerManager, IWebClient webClient, Logger logger, IProtectionService protectionService)
|
||||
: base(name: "EuTorrents",
|
||||
public CinemaZ(IIndexerManagerService indexerManager, IWebClient webClient, Logger logger, IProtectionService protectionService)
|
||||
: base(name: "CinemaZ",
|
||||
desc: "Part of the Avistaz network.",
|
||||
link: "https://eutorrents.to/",
|
||||
link: "https://cinemaz.to/",
|
||||
indexerManager: indexerManager,
|
||||
logger: logger,
|
||||
protectionService: protectionService,
|
@@ -1,141 +1,147 @@
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class Demonoid : BaseIndexer, IIndexer
|
||||
{
|
||||
private string LoginUrl { get { return SiteLink + "account_handler.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "files/?category={0}&subcategory=All&quality=All&seeded=0&to=1&query={1}"; } }
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public Demonoid(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "Demonoid",
|
||||
description: "Demonoid",
|
||||
link: "http://www.demonoid.pw/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
AddCategoryMapping(3, TorznabCatType.TV);
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class Demonoid : BaseIndexer, IIndexer
|
||||
{
|
||||
private string LoginUrl { get { return SiteLink + "account_handler.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "files/?category={0}&subcategory=All&quality=All&seeded=0&to=1&query={1}"; } }
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public Demonoid(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "Demonoid",
|
||||
description: "Demonoid",
|
||||
link: "http://www.dnoid.me/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
AddCategoryMapping(3, TorznabCatType.TV);
|
||||
AddCategoryMapping(3, TorznabCatType.TVSD);
|
||||
AddCategoryMapping(3, TorznabCatType.TVHD);
|
||||
AddCategoryMapping(1, TorznabCatType.Movies);
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "nickname", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "returnpath", "/" },
|
||||
{ "withq", "0" },
|
||||
{ "Submit", "Submit" }
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, SiteLink);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("user_control_panel.php"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var errorMessage = dom[".red"].ElementAt(1).Cq().Text().Trim();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var trackerCats = MapTorznabCapsToTrackers(query);
|
||||
var cat = (trackerCats.Count == 1 ? trackerCats.ElementAt(0) : "0");
|
||||
AddCategoryMapping(1, TorznabCatType.Movies);
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "nickname", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "returnpath", "/" },
|
||||
{ "withq", "0" },
|
||||
{ "Submit", "Submit" }
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, SiteLink);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("user_control_panel.php"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
string errorMessage = dom["form[id='bb_code_form']"].Parent().Find("font[class='red']").Text();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var trackerCats = MapTorznabCapsToTrackers(query);
|
||||
var cat = (trackerCats.Count == 1 ? trackerCats.ElementAt(0) : "0");
|
||||
var episodeSearchUrl = string.Format(SearchUrl, cat, HttpUtility.UrlEncode(query.GetQueryString()));
|
||||
var results = await RequestStringWithCookiesAndRetry(episodeSearchUrl);
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(episodeSearchUrl);
|
||||
|
||||
|
||||
if (results.Content.Contains("No torrents found"))
|
||||
{
|
||||
return releases;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
CQ dom = results.Content;
|
||||
var rows = dom[".ctable_content_no_pad > table > tbody > tr"].ToArray();
|
||||
DateTime lastDateTime = default(DateTime);
|
||||
for (var i = 0; i < rows.Length; i++)
|
||||
{
|
||||
var rowA = rows[i];
|
||||
var rAlign = rowA.Attributes["align"];
|
||||
if (rAlign == "right" || rAlign == "center")
|
||||
continue;
|
||||
if (rAlign == "left")
|
||||
{
|
||||
// ex: "Monday, Jun 01, 2015", "Monday, Aug 03, 2015"
|
||||
var dateStr = rowA.Cq().Text().Trim().Replace("Added on ", "");
|
||||
if (dateStr.ToLowerInvariant().Contains("today"))
|
||||
lastDateTime = DateTime.Now;
|
||||
else
|
||||
lastDateTime = DateTime.SpecifyKind(DateTime.ParseExact(dateStr, "dddd, MMM dd, yyyy", CultureInfo.InvariantCulture), DateTimeKind.Utc).ToLocalTime();
|
||||
continue;
|
||||
}
|
||||
if (rowA.ChildElements.Count() < 2)
|
||||
continue;
|
||||
|
||||
var rowB = rows[++i];
|
||||
|
||||
var release = new ReleaseInfo();
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800;
|
||||
|
||||
release.PublishDate = lastDateTime;
|
||||
|
||||
var qLink = rowA.ChildElements.ElementAt(1).FirstElementChild.Cq();
|
||||
release.Title = qLink.Text().Trim();
|
||||
release.Description = release.Title;
|
||||
|
||||
release.Comments = new Uri(SiteLink + qLink.Attr("href"));
|
||||
release.Guid = release.Comments;
|
||||
|
||||
var qDownload = rowB.ChildElements.ElementAt(2).ChildElements.ElementAt(1).Cq();
|
||||
release.Link = new Uri(SiteLink + qDownload.Attr("href"));
|
||||
|
||||
var sizeStr = rowB.ChildElements.ElementAt(3).Cq().Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(rowB.ChildElements.ElementAt(6).Cq().Text());
|
||||
release.Peers = ParseUtil.CoerceInt(rowB.ChildElements.ElementAt(6).Cq().Text()) + release.Seeders;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (results.Content.Contains("No torrents found"))
|
||||
{
|
||||
return releases;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
CQ dom = results.Content;
|
||||
var rows = dom[".ctable_content_no_pad > table > tbody > tr"].ToArray();
|
||||
DateTime lastDateTime = default(DateTime);
|
||||
for (var i = 0; i < rows.Length; i++)
|
||||
{
|
||||
var rowA = rows[i];
|
||||
var rAlign = rowA.Attributes["align"];
|
||||
if (rAlign == "right" || rAlign == "center")
|
||||
continue;
|
||||
if (rAlign == "left")
|
||||
{
|
||||
// ex: "Monday, Jun 01, 2015", "Monday, Aug 03, 2015"
|
||||
var dateStr = rowA.Cq().Text().Trim().Replace("Added on ", "");
|
||||
if (dateStr.ToLowerInvariant().Contains("today"))
|
||||
lastDateTime = DateTime.Now;
|
||||
else
|
||||
lastDateTime = DateTime.SpecifyKind(DateTime.ParseExact(dateStr, "dddd, MMM dd, yyyy", CultureInfo.InvariantCulture), DateTimeKind.Utc).ToLocalTime();
|
||||
continue;
|
||||
}
|
||||
if (rowA.ChildElements.Count() < 2)
|
||||
continue;
|
||||
|
||||
var rowB = rows[++i];
|
||||
|
||||
var release = new ReleaseInfo();
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800;
|
||||
|
||||
release.PublishDate = lastDateTime;
|
||||
|
||||
var qLink = rowA.ChildElements.ElementAt(1).FirstElementChild.Cq();
|
||||
release.Title = qLink.Text().Trim();
|
||||
release.Description = release.Title;
|
||||
|
||||
release.Comments = new Uri(SiteLink + qLink.Attr("href"));
|
||||
release.Guid = release.Comments;
|
||||
|
||||
var qDownload = rowB.ChildElements.ElementAt(2).ChildElements.ElementAt(0).Cq();
|
||||
release.Link = new Uri(SiteLink + qDownload.Attr("href"));
|
||||
|
||||
var sizeStr = rowB.ChildElements.ElementAt(3).Cq().Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(rowB.ChildElements.ElementAt(6).Cq().Text());
|
||||
release.Peers = ParseUtil.CoerceInt(rowB.ChildElements.ElementAt(6).Cq().Text()) + release.Seeders;
|
||||
|
||||
var grabs = rowB.Cq().Find("td:nth-child(6)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
release.DownloadVolumeFactor = 0; // ratioless
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
219
src/Jackett/Indexers/DigitalHive.cs
Normal file
@@ -0,0 +1,219 @@
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class DigitalHive : BaseIndexer, IIndexer
|
||||
{
|
||||
private string SearchUrl { get { return SiteLink + "browse.php"; } }
|
||||
private string LoginUrl { get { return SiteLink + "login.php?returnto=%2F"; } }
|
||||
private string AjaxLoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
|
||||
new ConfigurationDataRecaptchaLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataRecaptchaLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public DigitalHive(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps)
|
||||
: base(name: "DigitalHive",
|
||||
description: "DigitalHive is one of the oldest general trackers",
|
||||
link: "https://www.digitalhive.org/",
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataRecaptchaLogin())
|
||||
{
|
||||
AddCategoryMapping(0, TorznabCatType.Other);
|
||||
AddCategoryMapping(48, TorznabCatType.Other); // 0Day
|
||||
AddCategoryMapping(56, TorznabCatType.XXXImageset); // 0Day-Imagesets
|
||||
AddCategoryMapping(6, TorznabCatType.Audio); // 0Day-Music
|
||||
AddCategoryMapping(51, TorznabCatType.XXX); // 0Day-XXX
|
||||
AddCategoryMapping(2, TorznabCatType.TVAnime); // Anime
|
||||
AddCategoryMapping(59, TorznabCatType.MoviesBluRay); // BluRay
|
||||
AddCategoryMapping(40, TorznabCatType.TVDocumentary); // Documentary
|
||||
AddCategoryMapping(20, TorznabCatType.MoviesDVD); // DVD-R
|
||||
AddCategoryMapping(25, TorznabCatType.BooksEbook); // Ebooks
|
||||
AddCategoryMapping(38, TorznabCatType.PCPhoneIOS); // HandHeld
|
||||
AddCategoryMapping(38, TorznabCatType.PCPhoneAndroid); // HandHeld
|
||||
AddCategoryMapping(38, TorznabCatType.PCPhoneOther); // HandHeld
|
||||
AddCategoryMapping(37, TorznabCatType.Other); // Kids Stuff
|
||||
AddCategoryMapping(23, TorznabCatType.PC); // Linux
|
||||
AddCategoryMapping(24, TorznabCatType.PCMac); // Mac
|
||||
AddCategoryMapping(22, TorznabCatType.OtherMisc); // Misc
|
||||
AddCategoryMapping(35, TorznabCatType.MoviesOther); // Movie Pack
|
||||
AddCategoryMapping(36, TorznabCatType.MoviesHD); // Movie-HD
|
||||
AddCategoryMapping(19, TorznabCatType.MoviesSD); // Movie-SD
|
||||
AddCategoryMapping(50, TorznabCatType.Audio); // Music
|
||||
AddCategoryMapping(53, TorznabCatType.AudioLossless); // Music-FLAC
|
||||
AddCategoryMapping(49, TorznabCatType.AudioVideo); // MVID
|
||||
AddCategoryMapping(1, TorznabCatType.PC); // PC Apps
|
||||
AddCategoryMapping(4, TorznabCatType.PCGames); // PC Games
|
||||
AddCategoryMapping(17, TorznabCatType.ConsolePS3); // Playstation
|
||||
AddCategoryMapping(17, TorznabCatType.ConsolePS4); // Playstation
|
||||
AddCategoryMapping(17, TorznabCatType.ConsolePSVita); // Playstation
|
||||
AddCategoryMapping(17, TorznabCatType.ConsolePSP); // Playstation
|
||||
AddCategoryMapping(28, TorznabCatType.ConsolePSP); // PSP
|
||||
AddCategoryMapping(34, TorznabCatType.TVOTHER); // TV Pack
|
||||
AddCategoryMapping(32, TorznabCatType.TVHD); // TV-HD
|
||||
AddCategoryMapping(55, TorznabCatType.TVOTHER); // TV-HDRip
|
||||
AddCategoryMapping(7, TorznabCatType.TVSD); // TV-SD
|
||||
AddCategoryMapping(57, TorznabCatType.TVOTHER); // TV-SDRip
|
||||
AddCategoryMapping(33, TorznabCatType.ConsoleWii); // WII
|
||||
AddCategoryMapping(33, TorznabCatType.ConsoleWiiU); // WII
|
||||
AddCategoryMapping(45, TorznabCatType.ConsoleXbox); // XBox
|
||||
AddCategoryMapping(45, TorznabCatType.ConsoleXbox360); // XBox
|
||||
AddCategoryMapping(45, TorznabCatType.ConsoleXBOX360DLC); // XBox
|
||||
AddCategoryMapping(45, TorznabCatType.ConsoleXboxOne); // XBox
|
||||
AddCategoryMapping(9, TorznabCatType.XXX); // XXX
|
||||
AddCategoryMapping(52, TorznabCatType.XXXOther); // XXX-ISO
|
||||
}
|
||||
|
||||
public override async Task<ConfigurationData> GetConfigurationForSetup()
|
||||
{
|
||||
var loginPage = await RequestStringWithCookies(LoginUrl, configData.CookieHeader.Value);
|
||||
CQ cq = loginPage.Content;
|
||||
string recaptchaSiteKey = cq.Find(".g-recaptcha").Attr("data-sitekey");
|
||||
var result = this.configData;
|
||||
result.CookieHeader.Value = loginPage.Cookies;
|
||||
result.Captcha.SiteKey = recaptchaSiteKey;
|
||||
result.Captcha.Version = "2";
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "returnto" , "/" },
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "g-recaptcha-response", configData.Captcha.Value }
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(configData.Captcha.Cookie))
|
||||
{
|
||||
// Cookie was manually supplied
|
||||
CookieHeader = configData.Captcha.Cookie;
|
||||
try
|
||||
{
|
||||
var results = await PerformQuery(new TorznabQuery());
|
||||
if (!results.Any())
|
||||
{
|
||||
throw new Exception("Your cookie did not work");
|
||||
}
|
||||
|
||||
SaveConfig();
|
||||
IsConfigured = true;
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
IsConfigured = false;
|
||||
throw new Exception("Your cookie did not work: " + e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(AjaxLoginUrl, pairs, configData.CookieHeader.Value, true, SiteLink, LoginUrl);
|
||||
|
||||
await ConfigureIfOK(result.Cookies, result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
var errorMessage = result.Content;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
List<ReleaseInfo> releases = new List<ReleaseInfo>();
|
||||
|
||||
var queryCollection = new NameValueCollection();
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = SearchUrl;
|
||||
|
||||
foreach (var cat in MapTorznabCapsToTrackers(query))
|
||||
{
|
||||
queryCollection.Add("c" + cat, "1");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
queryCollection.Add("search", searchString);
|
||||
}
|
||||
|
||||
queryCollection.Add("blah", "0");
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(searchUrl + "?" + queryCollection.GetQueryString());
|
||||
try
|
||||
{
|
||||
releases.AddRange(contentToReleaseInfos(results.Content));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
private IEnumerable<ReleaseInfo> contentToReleaseInfos(CQ dom)
|
||||
{
|
||||
List<ReleaseInfo> releases = new List<ReleaseInfo>();
|
||||
|
||||
// Doesn't handle pagination yet...
|
||||
var rows = dom["div.panel-body > table.table > tbody > tr"];
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 259200;
|
||||
|
||||
var qRow = row.Cq();
|
||||
release.Title = qRow.Find("td:nth-child(2) > a").First().Text().Trim();
|
||||
release.Description = release.Title;
|
||||
release.Guid = new Uri(SiteLink + qRow.Find("td:nth-child(2) > a").First().Attr("href"));
|
||||
release.Comments = release.Guid;
|
||||
release.Link = new Uri(SiteLink + qRow.Find("td:nth-child(3) > a").First().Attr("href"));
|
||||
var pubDate = qRow.Find("td:nth-child(2) > span").First().Text().Trim().Replace("Added: ", "");
|
||||
release.PublishDate = DateTime.Parse(pubDate).ToLocalTime();
|
||||
release.Category = MapTrackerCatToNewznab(qRow.Find("td:nth-child(1) > a").First().Attr("href").Split('=')[1]);
|
||||
release.Size = ReleaseInfo.GetBytes(qRow.Find("td:nth-child(7)").First().Text());
|
||||
release.Seeders = ParseUtil.CoerceInt(qRow.Find("td:nth-child(9)").First().Text());
|
||||
release.Peers = ParseUtil.CoerceInt(qRow.Find("td:nth-child(10)").First().Text()) + release.Seeders;
|
||||
|
||||
var files = row.Cq().Find("td:nth-child(5)").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = row.Cq().Find("td:nth-child(8)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (row.Cq().Find("i.fa-star").Any())
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,966 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Models.IndexerConfig.Bespoke;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
/// <summary>
|
||||
/// Provider for French-ADN Private Tracker
|
||||
/// </summary>
|
||||
public class FrenchADN : BaseIndexer, IIndexer
|
||||
{
|
||||
private string LoginUrl { get { return SiteLink + "login.php?"; } }
|
||||
private string LoginCheckUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "browse.php"; } }
|
||||
private string TorrentCommentUrl { get { return SiteLink + "details.php?id={id}#comments"; } }
|
||||
private string TorrentDescriptionUrl { get { return SiteLink + "details.php?id={id}"; } }
|
||||
private string TorrentDownloadUrl { get { return SiteLink + "download.php?id={id}"; } }
|
||||
private string TorrentThanksUrl { get { return SiteLink + "takethanks.php"; } }
|
||||
private bool Latency { get { return ConfigData.Latency.Value; } }
|
||||
private bool DevMode { get { return ConfigData.DevMode.Value; } }
|
||||
private bool CacheMode { get { return ConfigData.HardDriveCache.Value; } }
|
||||
private string directory { get { return System.IO.Path.GetTempPath() + "Jackett\\" + MethodBase.GetCurrentMethod().DeclaringType.Name + "\\"; } }
|
||||
|
||||
private Dictionary<string, string> emulatedBrowserHeaders = new Dictionary<string, string>();
|
||||
private CQ fDom = null;
|
||||
|
||||
private ConfigurationDataFrenchADN ConfigData
|
||||
{
|
||||
get { return (ConfigurationDataFrenchADN)configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public FrenchADN(IIndexerManagerService i, IWebClient w, Logger l, IProtectionService ps)
|
||||
: base(
|
||||
name: "French-ADN",
|
||||
description: "Your French Family Provider",
|
||||
link: "https://french-adn.com/",
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
downloadBase: "https://french-adn.com/download.php?id=",
|
||||
configData: new ConfigurationDataFrenchADN())
|
||||
{
|
||||
// Clean capabilities
|
||||
TorznabCaps.Categories.Clear();
|
||||
|
||||
// Movies
|
||||
AddCategoryMapping("15", TorznabCatType.Movies); // ALL
|
||||
AddCategoryMapping("108", TorznabCatType.MoviesSD); // TS CAM
|
||||
AddCategoryMapping("25", TorznabCatType.MoviesSD); // BDRIP
|
||||
AddCategoryMapping("56", TorznabCatType.MoviesSD); // BRRIP
|
||||
AddCategoryMapping("16", TorznabCatType.MoviesSD); // DVDRIP
|
||||
AddCategoryMapping("49", TorznabCatType.MoviesDVD); // TVRIP
|
||||
AddCategoryMapping("102", TorznabCatType.MoviesWEBDL); // WEBRIP
|
||||
AddCategoryMapping("105", TorznabCatType.MoviesHD); // 1080P
|
||||
AddCategoryMapping("104", TorznabCatType.MoviesHD); // 720P
|
||||
AddCategoryMapping("17", TorznabCatType.MoviesDVD); // DVD R
|
||||
AddCategoryMapping("21", TorznabCatType.MoviesDVD); // DVD R5
|
||||
AddCategoryMapping("112", TorznabCatType.MoviesDVD); // DVD REMUX
|
||||
AddCategoryMapping("107", TorznabCatType.Movies3D); // 3D
|
||||
AddCategoryMapping("113", TorznabCatType.MoviesBluRay); // BLURAY
|
||||
AddCategoryMapping("118", TorznabCatType.MoviesHD); // MHD
|
||||
|
||||
// Series
|
||||
AddCategoryMapping("41", TorznabCatType.TV); // ALL
|
||||
AddCategoryMapping("43", TorznabCatType.TV); // VF
|
||||
AddCategoryMapping("44", TorznabCatType.TV); // VOSTFR
|
||||
AddCategoryMapping("42", TorznabCatType.TV); // PACK
|
||||
|
||||
// TV
|
||||
AddCategoryMapping("110", TorznabCatType.TV); // SHOWS
|
||||
|
||||
// Anime
|
||||
AddCategoryMapping("109", TorznabCatType.TVAnime); // ANIME
|
||||
|
||||
// Manga
|
||||
AddCategoryMapping("119", TorznabCatType.TVAnime); // MANGA
|
||||
|
||||
// Documentaries
|
||||
AddCategoryMapping("114", TorznabCatType.TVDocumentary); // DOCUMENTARY
|
||||
|
||||
// Music
|
||||
AddCategoryMapping("22", TorznabCatType.Audio); // ALL
|
||||
AddCategoryMapping("24", TorznabCatType.AudioLossless); // FLAC
|
||||
AddCategoryMapping("23", TorznabCatType.AudioMP3); // MP3
|
||||
|
||||
// Games
|
||||
AddCategoryMapping("33", TorznabCatType.PCGames); // ALL
|
||||
AddCategoryMapping("45", TorznabCatType.PCGames); // PC GAMES
|
||||
AddCategoryMapping("93", TorznabCatType.Console3DS); // 3DS
|
||||
AddCategoryMapping("94", TorznabCatType.Console); // PS2
|
||||
AddCategoryMapping("93", TorznabCatType.ConsolePS3); // PS3
|
||||
AddCategoryMapping("95", TorznabCatType.ConsolePSP); // PSP
|
||||
AddCategoryMapping("35", TorznabCatType.ConsolePS3); // WII
|
||||
|
||||
// Applications
|
||||
AddCategoryMapping("11", TorznabCatType.PC); // ALL
|
||||
AddCategoryMapping("12", TorznabCatType.PC); // APPS WINDOWS
|
||||
AddCategoryMapping("97", TorznabCatType.PCMac); // APPS MAC
|
||||
AddCategoryMapping("98", TorznabCatType.PC); // APPS LINUX
|
||||
|
||||
// Books
|
||||
AddCategoryMapping("115", TorznabCatType.BooksEbook); // EBOOK
|
||||
AddCategoryMapping("114", TorznabCatType.BooksComics); // COMICS
|
||||
|
||||
// Other
|
||||
AddCategoryMapping("103", TorznabCatType.Other); // STAFF
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure our FADN Provider
|
||||
/// </summary>
|
||||
/// <param name="configJson">Our params in Json</param>
|
||||
/// <returns>Configuration state</returns>
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
// Retrieve config values set by Jackett's user
|
||||
ConfigData.LoadValuesFromJson(configJson);
|
||||
|
||||
// Check & Validate Config
|
||||
validateConfig();
|
||||
|
||||
// Setting our data for a better emulated browser (maximum security)
|
||||
// TODO: Encoded Content not supported by Jackett at this time
|
||||
// emulatedBrowserHeaders.Add("Accept-Encoding", "gzip, deflate");
|
||||
|
||||
// If we want to simulate a browser
|
||||
if (ConfigData.Browser.Value)
|
||||
{
|
||||
|
||||
// Clean headers
|
||||
emulatedBrowserHeaders.Clear();
|
||||
|
||||
// Inject headers
|
||||
emulatedBrowserHeaders.Add("Accept", ConfigData.HeaderAccept.Value);
|
||||
emulatedBrowserHeaders.Add("Accept-Language", ConfigData.HeaderAcceptLang.Value);
|
||||
emulatedBrowserHeaders.Add("DNT", Convert.ToInt32(ConfigData.HeaderDNT.Value).ToString());
|
||||
emulatedBrowserHeaders.Add("Upgrade-Insecure-Requests", Convert.ToInt32(ConfigData.HeaderUpgradeInsecure.Value).ToString());
|
||||
emulatedBrowserHeaders.Add("User-Agent", ConfigData.HeaderUserAgent.Value);
|
||||
}
|
||||
|
||||
// Build WebRequest for index
|
||||
var myIndexRequest = new WebRequest()
|
||||
{
|
||||
Type = RequestType.GET,
|
||||
Url = SiteLink,
|
||||
Headers = emulatedBrowserHeaders
|
||||
};
|
||||
|
||||
// Get index page for cookies
|
||||
output("\nGetting index page (for cookies).. with " + SiteLink);
|
||||
var indexPage = await webclient.GetString(myIndexRequest);
|
||||
|
||||
// Building login form data
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", ConfigData.Username.Value },
|
||||
{ "password", ConfigData.Password.Value }
|
||||
};
|
||||
|
||||
// Build WebRequest for login
|
||||
var myRequestLogin = new WebRequest()
|
||||
{
|
||||
Type = RequestType.GET,
|
||||
Url = LoginUrl,
|
||||
Headers = emulatedBrowserHeaders,
|
||||
Cookies = indexPage.Cookies,
|
||||
Referer = SiteLink
|
||||
};
|
||||
|
||||
// Get login page -- (not used, but simulation needed by tracker security's checks)
|
||||
latencyNow();
|
||||
output("\nGetting login page (user simulation).. with " + LoginUrl);
|
||||
var loginPage = await webclient.GetString(myRequestLogin);
|
||||
|
||||
// Build WebRequest for submitting authentification
|
||||
var request = new WebRequest()
|
||||
{
|
||||
PostData = pairs,
|
||||
Referer = LoginUrl,
|
||||
Type = RequestType.POST,
|
||||
Url = LoginCheckUrl,
|
||||
Headers = emulatedBrowserHeaders,
|
||||
Cookies = indexPage.Cookies,
|
||||
|
||||
};
|
||||
|
||||
// Perform loggin
|
||||
latencyNow();
|
||||
output("\nPerform loggin.. with " + LoginCheckUrl);
|
||||
var response = await webclient.GetString(request);
|
||||
|
||||
// Test if we are logged in
|
||||
await ConfigureIfOK(response.Cookies, !string.IsNullOrEmpty(response.Cookies) && !response.IsRedirect, () =>
|
||||
{
|
||||
// Default error message
|
||||
string message = "Error during attempt !";
|
||||
|
||||
// Parse redirect header
|
||||
string redirectTo = response.RedirectingTo;
|
||||
|
||||
// Analyzer error code
|
||||
if(redirectTo.Contains("login.php?error=4"))
|
||||
{
|
||||
// Set message
|
||||
message = "Wrong username or password !";
|
||||
}
|
||||
|
||||
// Oops, unable to login
|
||||
output("-> Login failed: " + message, "error");
|
||||
throw new ExceptionWithConfigData("Login failed: " + message, configData);
|
||||
});
|
||||
|
||||
output("\nCookies saved for future uses...");
|
||||
ConfigData.CookieHeader.Value = indexPage.Cookies + " " + response.Cookies + " ts_username=" + ConfigData.Username.Value;
|
||||
|
||||
output("\n-> Login Success\n");
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute our search query
|
||||
/// </summary>
|
||||
/// <param name="query">Query</param>
|
||||
/// <returns>Releases</returns>
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var torrentRowList = new List<CQ>();
|
||||
var searchTerm = query.GetQueryString();
|
||||
var searchUrl = SearchUrl;
|
||||
int nbResults = 0;
|
||||
int pageLinkCount = 0;
|
||||
|
||||
// Check cache first so we don't query the server (if search term used or not in dev mode)
|
||||
if (!DevMode && !string.IsNullOrEmpty(searchTerm))
|
||||
{
|
||||
lock (cache)
|
||||
{
|
||||
// Remove old cache items
|
||||
CleanCache();
|
||||
|
||||
// Search in cache
|
||||
var cachedResult = cache.Where(i => i.Query == searchTerm).FirstOrDefault();
|
||||
if (cachedResult != null)
|
||||
return cachedResult.Results.Select(s => (ReleaseInfo)s.Clone()).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
// Build our query
|
||||
var request = buildQuery(searchTerm, query, searchUrl);
|
||||
|
||||
// Getting results & Store content
|
||||
WebClientStringResult results = await queryExec(request);
|
||||
fDom = results.Content;
|
||||
|
||||
try
|
||||
{
|
||||
// Find torrent rows
|
||||
var firstPageRows = findTorrentRows();
|
||||
|
||||
// Add them to torrents list
|
||||
torrentRowList.AddRange(firstPageRows.Select(fRow => fRow.Cq()));
|
||||
|
||||
// Check if there are pagination links at bottom
|
||||
Boolean pagination = (fDom["#quicknavpage_menu"].Length != 0);
|
||||
|
||||
// If pagination available
|
||||
if (pagination)
|
||||
{
|
||||
// Retrieve available pages (3 pages shown max)
|
||||
pageLinkCount = fDom["#navcontainer_f:first > ul"].Find("a").Not(".smalltext").Not("#quicknavpage").Length;
|
||||
|
||||
// Last button ? (So more than 3 page are available)
|
||||
Boolean more = (fDom["#navcontainer_f:first > ul"].Find("a.smalltext").Length > 1); ;
|
||||
|
||||
// More page than 3 pages ?
|
||||
if (more)
|
||||
{
|
||||
// Get total page count from last link
|
||||
pageLinkCount = ParseUtil.CoerceInt(Regex.Match(fDom["#navcontainer_f:first > ul"].Find("a:eq(4)").Attr("href").ToString(), @"\d+").Value);
|
||||
}
|
||||
|
||||
// Calculate average number of results (based on torrents rows lenght on first page)
|
||||
nbResults = firstPageRows.Count() * pageLinkCount;
|
||||
}
|
||||
else {
|
||||
nbResults = 1;
|
||||
pageLinkCount = 1;
|
||||
|
||||
// Check if we have a minimum of one result
|
||||
if (firstPageRows.Length > 1)
|
||||
{
|
||||
// Retrieve total count on our alone page
|
||||
nbResults = firstPageRows.Count();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if no result
|
||||
if(torrentRowList.First().Find("td").Length == 1)
|
||||
{
|
||||
// No results found
|
||||
output("\nNo result found for your query, please try another search term ...\n", "info");
|
||||
|
||||
// No result found for this query
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
||||
output("\nFound " + nbResults + " result(s) (+/- " + firstPageRows.Length + ") in " + pageLinkCount + " page(s) for this query !");
|
||||
output("\nThere are " + firstPageRows.Length + " results on the first page !");
|
||||
|
||||
// If we have a term used for search and pagination result superior to one
|
||||
if (!string.IsNullOrWhiteSpace(query.GetQueryString()) && pageLinkCount > 1)
|
||||
{
|
||||
// Starting with page #2
|
||||
for (int i = 2; i <= Math.Min(Int32.Parse(ConfigData.Pages.Value), pageLinkCount); i++)
|
||||
{
|
||||
output("\nProcessing page #" + i);
|
||||
|
||||
// Request our page
|
||||
latencyNow();
|
||||
|
||||
// Build our query -- Minus 1 to page due to strange pagination number on tracker side, starting with page 0...
|
||||
var pageRequest = buildQuery(searchTerm, query, searchUrl, i);
|
||||
|
||||
// Getting results & Store content
|
||||
WebClientStringResult pageResults = await queryExec(pageRequest);
|
||||
|
||||
// Assign response
|
||||
fDom = pageResults.Content;
|
||||
|
||||
// Process page results
|
||||
var additionalPageRows = findTorrentRows();
|
||||
|
||||
// Add them to torrents list
|
||||
torrentRowList.AddRange(additionalPageRows.Select(fRow => fRow.Cq()));
|
||||
}
|
||||
}
|
||||
|
||||
// Loop on results
|
||||
foreach (CQ tRow in torrentRowList)
|
||||
{
|
||||
output("\n=>> Torrent #" + (releases.Count + 1));
|
||||
|
||||
// ID
|
||||
int id = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(1) > div:first > a").Attr("name").ToString(), @"\d+").Value);
|
||||
output("ID: " + id);
|
||||
|
||||
// Check if torrent is not nuked by tracker or rulez, can't download it
|
||||
if (tRow.Find("td:eq(2) > a").Length == 0)
|
||||
{
|
||||
// Next item
|
||||
output("Torrent is nuked, we can't download it, going to next torrent...");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Release Name
|
||||
string name = tRow.Find("td:eq(2) > a").Attr("title").ToString().Substring(24).Trim();
|
||||
output("Release: " + name);
|
||||
|
||||
// Category
|
||||
int categoryID = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(0) > a").Attr("href").ToString(), @"\d+").Value);
|
||||
string categoryName = tRow.Find("td:eq(0) > a > img").Attr("title").Split(new[] { ':' }, 2)[1].Trim().ToString();
|
||||
output("Category: " + MapTrackerCatToNewznab(categoryID.ToString()) + " (" + categoryID + " - " + categoryName + ")");
|
||||
|
||||
// Seeders
|
||||
int seeders = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(5) > div > font").Select(s => Regex.Replace(s.ToString(), "<.*?>", String.Empty)).ToString(), @"\d+").Value);
|
||||
output("Seeders: " + seeders);
|
||||
|
||||
// Leechers
|
||||
int leechers = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(6) > div > font").Text().ToString(), @"\d+").Value);
|
||||
output("Leechers: " + leechers);
|
||||
|
||||
// Completed
|
||||
int completed = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(4)").Text().ToString(), @"\d+").Value);
|
||||
output("Completed: " + completed);
|
||||
|
||||
// Files
|
||||
int files = 1;
|
||||
if (tRow.Find("td:eq(3) > a").Length == 1)
|
||||
{
|
||||
files = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(3) > a").Text().ToString(), @"\d+").Value);
|
||||
}
|
||||
output("Files: " + files);
|
||||
|
||||
// Health
|
||||
int percent = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(7) > img").Attr("src").ToString(), @"\d+").Value) * 10;
|
||||
output("Health: " + percent + "%");
|
||||
|
||||
// Size
|
||||
string humanSize = tRow.Find("td:eq(8)").Text().ToString().ToLowerInvariant();
|
||||
long size = ReleaseInfo.GetBytes(humanSize);
|
||||
output("Size: " + humanSize + " (" + size + " bytes)");
|
||||
|
||||
// Date & IMDB & Genre
|
||||
string infosData = tRow.Find("td:eq(1) > div:last").Text().ToString();
|
||||
IList<string> infosList = Regex.Split(infosData, "\\|").ToList();
|
||||
IList<string> infosTorrent = infosList.Select(s => s.Split(new[] { ':' }, 2)[1].Trim()).ToList();
|
||||
|
||||
// --> Date
|
||||
DateTime date = formatDate(infosTorrent.First());
|
||||
output("Released on: " + date.ToLocalTime().ToString());
|
||||
|
||||
// --> Genre
|
||||
string genre = infosTorrent.Last();
|
||||
output("Genre: " + genre);
|
||||
|
||||
// Torrent Details URL
|
||||
Uri detailsLink = new Uri(TorrentDescriptionUrl.Replace("{id}", id.ToString()));
|
||||
output("Details: " + detailsLink.AbsoluteUri);
|
||||
|
||||
// Torrent Comments URL
|
||||
Uri commentsLink = new Uri(TorrentCommentUrl.Replace("{id}", id.ToString()));
|
||||
output("Comments Link: " + commentsLink.AbsoluteUri);
|
||||
|
||||
// Torrent Download URL
|
||||
Uri downloadLink = new Uri(TorrentDownloadUrl.Replace("{id}", id.ToString()));
|
||||
output("Download Link: " + downloadLink.AbsoluteUri);
|
||||
|
||||
// Building release infos
|
||||
var release = new ReleaseInfo();
|
||||
release.Category = MapTrackerCatToNewznab(categoryID.ToString());
|
||||
release.Title = name;
|
||||
release.Seeders = seeders;
|
||||
release.Peers = seeders + leechers;
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800;
|
||||
release.PublishDate = date;
|
||||
release.Size = size;
|
||||
release.Guid = detailsLink;
|
||||
release.Comments = commentsLink;
|
||||
release.Link = downloadLink;
|
||||
releases.Add(release);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError("Error, unable to parse result \n" + ex.StackTrace, ex);
|
||||
}
|
||||
|
||||
// Return found releases
|
||||
return releases;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build query to process
|
||||
/// </summary>
|
||||
/// <param name="term">Term to search</param>
|
||||
/// <param name="query">Torznab Query for categories mapping</param>
|
||||
/// <param name="url">Search url for provider</param>
|
||||
/// <param name="page">Page number to request</param>
|
||||
/// <returns>URL to query for parsing and processing results</returns>
|
||||
private string buildQuery(string term, TorznabQuery query, string url, int page = 0)
|
||||
{
|
||||
var parameters = new NameValueCollection();
|
||||
List<string> categoriesList = MapTorznabCapsToTrackers(query);
|
||||
|
||||
// Building our tracker query
|
||||
parameters.Add("do", "search");
|
||||
|
||||
// If search term provided
|
||||
if (!string.IsNullOrWhiteSpace(term))
|
||||
{
|
||||
// Add search term ~~ Strange search engine, need to replace space with dot for results !
|
||||
parameters.Add("keywords", term.Replace(' ', '.'));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Showing all torrents (just for output function)
|
||||
parameters.Add("keywords", "");
|
||||
term = "all";
|
||||
}
|
||||
|
||||
// Adding requested categories
|
||||
if(categoriesList.Count > 0)
|
||||
{
|
||||
// Add categories
|
||||
parameters.Add("category", String.Join(",", categoriesList));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add empty category parameter
|
||||
parameters.Add("category", "");
|
||||
}
|
||||
|
||||
// Building our tracker query
|
||||
parameters.Add("search_type", "t_name");
|
||||
|
||||
// Check if we are processing a new page
|
||||
if (page > 1)
|
||||
{
|
||||
// Adding page number to query
|
||||
parameters.Add("page", page.ToString());
|
||||
}
|
||||
|
||||
// Building our query
|
||||
url += "?" + parameters.GetQueryString();
|
||||
|
||||
output("\nBuilded query for \"" + term + "\"... " + url);
|
||||
|
||||
// Return our search url
|
||||
return url;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Switch Method for Querying
|
||||
/// </summary>
|
||||
/// <param name="request">URL created by Query Builder</param>
|
||||
/// <returns>Results from query</returns>
|
||||
private async Task<WebClientStringResult> queryExec(string request)
|
||||
{
|
||||
WebClientStringResult results = null;
|
||||
|
||||
// Switch in we are in DEV mode with Hard Drive Cache or not
|
||||
if (DevMode && CacheMode)
|
||||
{
|
||||
// Check Cache before querying and load previous results if available
|
||||
results = await queryCache(request);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Querying tracker directly
|
||||
results = await queryTracker(request);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Torrents Page from Cache by Query Provided
|
||||
/// </summary>
|
||||
/// <param name="request">URL created by Query Builder</param>
|
||||
/// <returns>Results from query</returns>
|
||||
private async Task<WebClientStringResult> queryCache(string request)
|
||||
{
|
||||
WebClientStringResult results = null;
|
||||
|
||||
// Create Directory if not exist
|
||||
System.IO.Directory.CreateDirectory(directory);
|
||||
|
||||
// Clean Storage Provider Directory from outdated cached queries
|
||||
cleanCacheStorage();
|
||||
|
||||
// Create fingerprint for request
|
||||
string file = directory + request.GetHashCode() + ".json";
|
||||
|
||||
// Checking modes states
|
||||
if (System.IO.File.Exists(file))
|
||||
{
|
||||
// File exist... loading it right now !
|
||||
output("Loading results from hard drive cache ..." + request.GetHashCode() + ".json");
|
||||
results = JsonConvert.DeserializeObject<WebClientStringResult>(System.IO.File.ReadAllText(file));
|
||||
}
|
||||
else
|
||||
{
|
||||
// No cached file found, querying tracker directly
|
||||
results = await queryTracker(request);
|
||||
|
||||
// Cached file didn't exist for our query, writing it right now !
|
||||
output("Writing results to hard drive cache ..." + request.GetHashCode() + ".json");
|
||||
System.IO.File.WriteAllText(file, JsonConvert.SerializeObject(results));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Torrents Page from Tracker by Query Provided
|
||||
/// </summary>
|
||||
/// <param name="request">URL created by Query Builder</param>
|
||||
/// <returns>Results from query</returns>
|
||||
private async Task<WebClientStringResult> queryTracker(string request)
|
||||
{
|
||||
WebClientStringResult results = null;
|
||||
|
||||
// Cache mode not enabled or cached file didn't exist for our query
|
||||
output("\nQuerying tracker for results....");
|
||||
|
||||
// Request our first page
|
||||
latencyNow();
|
||||
results = await RequestStringWithCookiesAndRetry(request, ConfigData.CookieHeader.Value, SearchUrl, emulatedBrowserHeaders);
|
||||
|
||||
// Return results from tracker
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean Hard Drive Cache Storage
|
||||
/// </summary>
|
||||
/// <param name="force">Force Provider Folder deletion</param>
|
||||
private void cleanCacheStorage(Boolean force = false)
|
||||
{
|
||||
// Check cleaning method
|
||||
if (force)
|
||||
{
|
||||
// Deleting Provider Storage folder and all files recursively
|
||||
output("\nDeleting Provider Storage folder and all files recursively ...");
|
||||
|
||||
// Check if directory exist
|
||||
if (System.IO.Directory.Exists(directory))
|
||||
{
|
||||
// Delete storage directory of provider
|
||||
System.IO.Directory.Delete(directory, true);
|
||||
output("-> Storage folder deleted successfully.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// No directory, so nothing to do
|
||||
output("-> No Storage folder found for this provider !");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = 0;
|
||||
// Check if there is file older than ... and delete them
|
||||
output("\nCleaning Provider Storage folder... in progress.");
|
||||
System.IO.Directory.GetFiles(directory)
|
||||
.Select(f => new System.IO.FileInfo(f))
|
||||
.Where(f => f.LastAccessTime < DateTime.Now.AddMilliseconds(-Convert.ToInt32(ConfigData.HardDriveCacheKeepTime.Value)))
|
||||
.ToList()
|
||||
.ForEach(f => {
|
||||
output("Deleting cached file << " + f.Name + " >> ... done.");
|
||||
f.Delete();
|
||||
i++;
|
||||
});
|
||||
|
||||
// Inform on what was cleaned during process
|
||||
if (i > 0)
|
||||
{
|
||||
output("-> Deleted " + i + " cached files during cleaning.");
|
||||
}
|
||||
else {
|
||||
output("-> Nothing deleted during cleaning.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a random fake latency to avoid detection on tracker side
|
||||
/// </summary>
|
||||
private void latencyNow()
|
||||
{
|
||||
// Need latency ?
|
||||
if (Latency)
|
||||
{
|
||||
// Generate a random value in our range
|
||||
var random = new Random(DateTime.Now.Millisecond);
|
||||
int waiting = random.Next(Convert.ToInt32(ConfigData.LatencyStart.Value), Convert.ToInt32(ConfigData.LatencyEnd.Value));
|
||||
output("\nLatency Faker => Sleeping for " + waiting + " ms...");
|
||||
|
||||
// Sleep now...
|
||||
System.Threading.Thread.Sleep(waiting);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find torrent rows in search pages
|
||||
/// </summary>
|
||||
/// <returns>JQuery Object</returns>
|
||||
private CQ findTorrentRows()
|
||||
{
|
||||
// Return all occurencis of torrents found
|
||||
return fDom["#showcontents > table > tbody > tr:not(:first)"];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Format Date to DateTime
|
||||
/// </summary>
|
||||
/// <param name="clock"></param>
|
||||
/// <returns>A DateTime</returns>
|
||||
private DateTime formatDate(string clock)
|
||||
{
|
||||
DateTime date;
|
||||
|
||||
// Switch from date format
|
||||
if(clock.Contains("Aujourd'hui") || clock.Contains("Hier"))
|
||||
{
|
||||
// Get hours & minutes
|
||||
IList<int> infosClock = clock.Split(':').Select(s => ParseUtil.CoerceInt(Regex.Match(s, @"\d+").Value)).ToList();
|
||||
|
||||
// Ago date with today
|
||||
date = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, Convert.ToInt32(infosClock[0]), Convert.ToInt32(infosClock[1]), DateTime.Now.Second);
|
||||
|
||||
// Set yesterday if necessary
|
||||
if (clock.Contains("Hier"))
|
||||
{
|
||||
// Remove one day from date
|
||||
date.AddDays(-1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Parse Date if full
|
||||
date = DateTime.ParseExact(clock, "MM-dd-yyyy HH:mm", CultureInfo.GetCultureInfo("fr-FR"), DateTimeStyles.AssumeLocal);
|
||||
}
|
||||
|
||||
return date.ToUniversalTime();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download torrent file from tracker
|
||||
/// </summary>
|
||||
/// <param name="link">URL string</param>
|
||||
/// <returns></returns>
|
||||
public async override Task<byte[]> Download(Uri link)
|
||||
{
|
||||
var dl = link.AbsoluteUri;
|
||||
// This tracker need to thanks Uploader before getting torrent file...
|
||||
output("\nThis tracker needs you to thank uploader before downloading torrent!");
|
||||
|
||||
// Retrieving ID from link provided
|
||||
int id = ParseUtil.CoerceInt(Regex.Match(link.AbsoluteUri, @"\d+").Value);
|
||||
output("Torrent Requested ID: " + id);
|
||||
|
||||
// Building login form data
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "torrentid", id.ToString() },
|
||||
{ "_", string.Empty } // ~~ Strange, blank param...
|
||||
};
|
||||
|
||||
// Add emulated XHR request
|
||||
emulatedBrowserHeaders.Add("X-Prototype-Version", "1.6.0.3");
|
||||
emulatedBrowserHeaders.Add("X-Requested-With", "XMLHttpRequest");
|
||||
|
||||
// Build WebRequest for thanks
|
||||
var myRequestThanks = new WebRequest()
|
||||
{
|
||||
Type = RequestType.POST,
|
||||
PostData = pairs,
|
||||
Url = TorrentThanksUrl,
|
||||
Headers = emulatedBrowserHeaders,
|
||||
Cookies = ConfigData.CookieHeader.Value,
|
||||
Referer = TorrentDescriptionUrl.Replace("{id}", id.ToString())
|
||||
};
|
||||
|
||||
// Get thanks page -- (not used, just for doing a request)
|
||||
latencyNow();
|
||||
output("Thanks user, to get download link for our torrent.. with " + TorrentThanksUrl);
|
||||
var thanksPage = await webclient.GetString(myRequestThanks);
|
||||
|
||||
// Get torrent file now
|
||||
output("Getting torrent file now....");
|
||||
var response = await base.Download(link);
|
||||
|
||||
// Remove our XHR request header
|
||||
emulatedBrowserHeaders.Remove("X-Prototype-Version");
|
||||
emulatedBrowserHeaders.Remove("X-Requested-With");
|
||||
|
||||
// Return content
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Output message for logging or developpment (console)
|
||||
/// </summary>
|
||||
/// <param name="message">Message to output</param>
|
||||
/// <param name="level">Level for Logger</param>
|
||||
private void output(string message, string level = "debug")
|
||||
{
|
||||
// Check if we are in dev mode
|
||||
if (DevMode)
|
||||
{
|
||||
// Output message to console
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send message to logger with level
|
||||
switch (level)
|
||||
{
|
||||
default:
|
||||
goto case "debug";
|
||||
case "debug":
|
||||
// Only if Debug Level Enabled on Jackett
|
||||
if (Engine.Logger.IsDebugEnabled)
|
||||
{
|
||||
logger.Debug(message);
|
||||
}
|
||||
break;
|
||||
case "info":
|
||||
logger.Info(message);
|
||||
break;
|
||||
case "error":
|
||||
logger.Error(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate Config entered by user on Jackett
|
||||
/// </summary>
|
||||
private void validateConfig()
|
||||
{
|
||||
output("\nValidating Settings ... \n");
|
||||
|
||||
// Check Username Setting
|
||||
if (string.IsNullOrEmpty(ConfigData.Username.Value))
|
||||
{
|
||||
throw new ExceptionWithConfigData("You must provide a username for this tracker to login !", ConfigData);
|
||||
}
|
||||
else
|
||||
{
|
||||
output("Validated Setting -- Username (auth) => " + ConfigData.Username.Value.ToString());
|
||||
}
|
||||
|
||||
// Check Password Setting
|
||||
if (string.IsNullOrEmpty(ConfigData.Password.Value))
|
||||
{
|
||||
throw new ExceptionWithConfigData("You must provide a password with your username for this tracker to login !", ConfigData);
|
||||
}
|
||||
else
|
||||
{
|
||||
output("Validated Setting -- Password (auth) => " + ConfigData.Password.Value.ToString());
|
||||
}
|
||||
|
||||
// Check Max Page Setting
|
||||
if (!string.IsNullOrEmpty(ConfigData.Pages.Value))
|
||||
{
|
||||
try
|
||||
{
|
||||
output("Validated Setting -- Max Pages => " + Convert.ToInt32(ConfigData.Pages.Value));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new ExceptionWithConfigData("Please enter a numeric maximum number of pages to crawl !", ConfigData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ExceptionWithConfigData("Please enter a maximum number of pages to crawl !", ConfigData);
|
||||
}
|
||||
|
||||
// Check Latency Setting
|
||||
if (ConfigData.Latency.Value)
|
||||
{
|
||||
output("\nValidated Setting -- Latency Simulation enabled");
|
||||
|
||||
// Check Latency Start Setting
|
||||
if (!string.IsNullOrEmpty(ConfigData.LatencyStart.Value))
|
||||
{
|
||||
try
|
||||
{
|
||||
output("Validated Setting -- Latency Start => " + Convert.ToInt32(ConfigData.LatencyStart.Value));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new ExceptionWithConfigData("Please enter a numeric latency start in ms !", ConfigData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ExceptionWithConfigData("Latency Simulation enabled, Please enter a start latency !", ConfigData);
|
||||
}
|
||||
|
||||
// Check Latency End Setting
|
||||
if (!string.IsNullOrEmpty(ConfigData.LatencyEnd.Value))
|
||||
{
|
||||
try
|
||||
{
|
||||
output("Validated Setting -- Latency End => " + Convert.ToInt32(ConfigData.LatencyEnd.Value));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new ExceptionWithConfigData("Please enter a numeric latency end in ms !", ConfigData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ExceptionWithConfigData("Latency Simulation enabled, Please enter a end latency !", ConfigData);
|
||||
}
|
||||
}
|
||||
|
||||
// Check Browser Setting
|
||||
if (ConfigData.Browser.Value == true)
|
||||
{
|
||||
output("\nValidated Setting -- Browser Simulation enabled");
|
||||
|
||||
// Check ACCEPT header Setting
|
||||
if (string.IsNullOrEmpty(ConfigData.HeaderAccept.Value))
|
||||
{
|
||||
throw new ExceptionWithConfigData("Browser Simulation enabled, Please enter an ACCEPT header !", ConfigData);
|
||||
}
|
||||
else
|
||||
{
|
||||
output("Validated Setting -- ACCEPT (header) => " + ConfigData.HeaderAccept.Value.ToString());
|
||||
}
|
||||
|
||||
// Check ACCEPT-LANG header Setting
|
||||
if (string.IsNullOrEmpty(ConfigData.HeaderAcceptLang.Value))
|
||||
{
|
||||
throw new ExceptionWithConfigData("Browser Simulation enabled, Please enter an ACCEPT-LANG header !", ConfigData);
|
||||
}
|
||||
else
|
||||
{
|
||||
output("Validated Setting -- ACCEPT-LANG (header) => " + ConfigData.HeaderAcceptLang.Value.ToString());
|
||||
}
|
||||
|
||||
// Check USER-AGENT header Setting
|
||||
if (string.IsNullOrEmpty(ConfigData.HeaderUserAgent.Value))
|
||||
{
|
||||
throw new ExceptionWithConfigData("Browser Simulation enabled, Please enter an USER-AGENT header !", ConfigData);
|
||||
}
|
||||
else
|
||||
{
|
||||
output("Validated Setting -- USER-AGENT (header) => " + ConfigData.HeaderUserAgent.Value.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Browser simulation must be enabled (otherwhise, this provider will not work due to tracker's security)
|
||||
throw new ExceptionWithConfigData("Browser Simulation must be enabled for this provider to work, please enable it !", ConfigData);
|
||||
}
|
||||
|
||||
// Check Dev Cache Settings
|
||||
if (ConfigData.HardDriveCache.Value == true)
|
||||
{
|
||||
output("\nValidated Setting -- DEV Hard Drive Cache enabled");
|
||||
|
||||
// Check if Dev Mode enabled !
|
||||
if (!ConfigData.DevMode.Value)
|
||||
{
|
||||
throw new ExceptionWithConfigData("Hard Drive is enabled but not in DEV MODE, Please enable DEV MODE !", ConfigData);
|
||||
}
|
||||
|
||||
// Check Cache Keep Time Setting
|
||||
if (!string.IsNullOrEmpty(ConfigData.HardDriveCacheKeepTime.Value))
|
||||
{
|
||||
try
|
||||
{
|
||||
output("Validated Setting -- Cache Keep Time (ms) => " + Convert.ToInt32(ConfigData.HardDriveCacheKeepTime.Value));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new ExceptionWithConfigData("Please enter a numeric hard drive keep time in ms !", ConfigData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ExceptionWithConfigData("Hard Drive Cache enabled, Please enter a maximum keep time for cache !", ConfigData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Delete cache if previously existed
|
||||
cleanCacheStorage(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,6 @@
|
||||
using Jackett.Models;
|
||||
using CsQuery;
|
||||
using Jackett.Indexers;
|
||||
using Jackett.Models;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
@@ -8,10 +10,14 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Web.UI.WebControls;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using AngleSharp;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
@@ -36,7 +42,7 @@ namespace Jackett.Indexers
|
||||
client: c,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin("For best results, change the 'Torrents per page' setting to 100 in your profile on the FreshOn webpage."))
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -54,32 +60,15 @@ namespace Jackett.Indexers
|
||||
|
||||
await ConfigureIfOK(response.Cookies, response.Content != null && response.Content.Contains("/logout.php"), () =>
|
||||
{
|
||||
var parser = new AngleSharp.Parser.Html.HtmlParser();
|
||||
var document = parser.Parse(response.Content);
|
||||
var messageEl = document.QuerySelector(".error_text");
|
||||
var errorMessage = messageEl.TextContent.Trim();
|
||||
CQ dom = response.Content;
|
||||
var messageEl = dom[".error_text"];
|
||||
var errorMessage = messageEl.Text().Trim();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
string Url;
|
||||
if (string.IsNullOrEmpty(query.GetQueryString()))
|
||||
Url = SearchUrl;
|
||||
else
|
||||
{
|
||||
Url = $"{SearchUrl}?search={HttpUtility.UrlEncode(query.GetQueryString())}&cat=0";
|
||||
}
|
||||
|
||||
var response = await RequestStringWithCookiesAndRetry(Url);
|
||||
List<ReleaseInfo> releases = ParseResponse(response.Content);
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
private List<ReleaseInfo> ParseResponse(string htmlResponse)
|
||||
{
|
||||
TimeZoneInfo.TransitionTime startTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 3, 0, 0), 3, 5, DayOfWeek.Sunday);
|
||||
TimeZoneInfo.TransitionTime endTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 4, 0, 0), 10, 5, DayOfWeek.Sunday);
|
||||
@@ -88,50 +77,78 @@ namespace Jackett.Indexers
|
||||
TimeZoneInfo.AdjustmentRule[] adjustments = { adjustment };
|
||||
TimeZoneInfo romaniaTz = TimeZoneInfo.CreateCustomTimeZone("Romania Time", new TimeSpan(2, 0, 0), "(GMT+02:00) Romania Time", "Romania Time", "Romania Daylight Time", adjustments);
|
||||
|
||||
List<ReleaseInfo> releases = new List<ReleaseInfo>();
|
||||
var releases = new List<ReleaseInfo>();
|
||||
string episodeSearchUrl;
|
||||
|
||||
if (string.IsNullOrEmpty(query.GetQueryString()))
|
||||
episodeSearchUrl = SearchUrl;
|
||||
else
|
||||
{
|
||||
episodeSearchUrl = $"{SearchUrl}?search={HttpUtility.UrlEncode(query.GetQueryString())}&cat=0";
|
||||
}
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(episodeSearchUrl);
|
||||
try
|
||||
{
|
||||
var parser = new AngleSharp.Parser.Html.HtmlParser();
|
||||
var document = parser.Parse(htmlResponse);
|
||||
var rows = document.QuerySelectorAll("#highlight > tbody > tr:not(:First-child)");
|
||||
CQ dom = results.Content;
|
||||
|
||||
foreach (var row in rows)
|
||||
var rows = dom["#highlight > tbody > tr"];
|
||||
|
||||
foreach (var row in rows.Skip(1))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
|
||||
var linkNameElement = row.QuerySelector("a.torrent_name_link");
|
||||
var qRow = row.Cq();
|
||||
var qLink = qRow.Find("a.torrent_name_link").First();
|
||||
|
||||
release.Title = linkNameElement.GetAttribute("title");
|
||||
release.Description = release.Title;
|
||||
release.Guid = new Uri(SiteLink + linkNameElement.GetAttribute("href"));
|
||||
release.Comments = release.Guid;
|
||||
release.Link = new Uri(SiteLink + row.QuerySelector("td.table_links > a").GetAttribute("href"));
|
||||
release.Category = TvCategoryParser.ParseTvShowQuality(release.Title);
|
||||
release.Seeders = ParseUtil.CoerceInt(row.QuerySelector("td.table_seeders").TextContent.Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(row.QuerySelector("td.table_leechers").TextContent.Trim()) + release.Seeders;
|
||||
release.Size = ReleaseInfo.GetBytes(row.QuerySelector("td.table_size").TextContent);
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800;
|
||||
release.Title = qLink.Attr("title");
|
||||
if (!query.MatchQueryStringAND(release.Title))
|
||||
continue;
|
||||
|
||||
release.Description = release.Title;
|
||||
release.Guid = new Uri(SiteLink + qLink.Attr("href").TrimStart('/'));
|
||||
release.Comments = release.Guid;
|
||||
release.Link = new Uri(SiteLink + qRow.Find("td.table_links > a").First().Attr("href").TrimStart('/'));
|
||||
release.Category = TvCategoryParser.ParseTvShowQuality(release.Title);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(qRow.Find("td.table_seeders").Text().Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(qRow.Find("td.table_leechers").Text().Trim()) + release.Seeders;
|
||||
|
||||
var sizeStr = qRow.Find("td.table_size")[0].Cq().Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
DateTime pubDateRomania;
|
||||
var dateString = row.QuerySelector("td.table_added").TextContent.Trim();
|
||||
var dateString = qRow.Find("td.table_added").Text().Trim();
|
||||
if (dateString.StartsWith("Today "))
|
||||
{ pubDateRomania = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) + TimeSpan.Parse(dateString.Split(' ')[1]); }
|
||||
{ pubDateRomania = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) + TimeSpan.Parse(dateString.Split(' ')[1]); }
|
||||
else if (dateString.StartsWith("Yesterday "))
|
||||
{ pubDateRomania = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) + TimeSpan.Parse(dateString.Split(' ')[1]) - TimeSpan.FromDays(1); }
|
||||
{ pubDateRomania = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) + TimeSpan.Parse(dateString.Split(' ')[1]) - TimeSpan.FromDays(1); }
|
||||
else
|
||||
{ pubDateRomania = DateTime.SpecifyKind(DateTime.ParseExact(dateString, "d-MMM-yyyy HH:mm:ss", CultureInfo.InvariantCulture), DateTimeKind.Unspecified); }
|
||||
{ pubDateRomania = DateTime.SpecifyKind(DateTime.ParseExact(dateString, "d-MMM-yyyy HH:mm:ss", CultureInfo.InvariantCulture), DateTimeKind.Unspecified); }
|
||||
|
||||
DateTime pubDateUtc = TimeZoneInfo.ConvertTimeToUtc(pubDateRomania, romaniaTz);
|
||||
release.PublishDate = pubDateUtc.ToLocalTime();
|
||||
|
||||
var grabs = row.Cq().Find("td.table_snatch").Get(0).FirstChild.ToString();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (row.Cq().Find("img[alt=\"100% Free\"]").Any())
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else if (row.Cq().Find("img[alt=\"50% Free\"]").Any())
|
||||
release.DownloadVolumeFactor = 0.5;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(htmlResponse, ex);
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
|
154
src/Jackett/Indexers/FunFile.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class FunFile : BaseIndexer, IIndexer
|
||||
{
|
||||
private string SearchUrl { get { return SiteLink + "browse.php"; } }
|
||||
private string LoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
|
||||
new ConfigurationDataBasicLoginWithRSSAndDisplay configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLoginWithRSSAndDisplay)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public FunFile(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps)
|
||||
: base(name: "FunFile",
|
||||
description: "A general tracker",
|
||||
link: "https://www.funfile.org/",
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLoginWithRSSAndDisplay())
|
||||
{
|
||||
AddCategoryMapping(44, TorznabCatType.TVAnime); // Anime
|
||||
AddCategoryMapping(22, TorznabCatType.PC); // Applications
|
||||
AddCategoryMapping(43, TorznabCatType.AudioAudiobook); // Audio Books
|
||||
AddCategoryMapping(27, TorznabCatType.Books); // Ebook
|
||||
AddCategoryMapping(4, TorznabCatType.PCGames); // Games
|
||||
AddCategoryMapping(40, TorznabCatType.OtherMisc); // Miscellaneous
|
||||
AddCategoryMapping(19, TorznabCatType.Movies); // Movies
|
||||
AddCategoryMapping(6, TorznabCatType.Audio); // Music
|
||||
AddCategoryMapping(31, TorznabCatType.PCPhoneOther); // Portable
|
||||
AddCategoryMapping(7, TorznabCatType.TV); // TV
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "login", "Login" },
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, LoginUrl);
|
||||
await ConfigureIfOK(result.Cookies, result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var errorMessage = dom["td.mf_content"].Html();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
List<ReleaseInfo> releases = new List<ReleaseInfo>();
|
||||
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = SearchUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
queryCollection.Add("incldead", "1");
|
||||
queryCollection.Add("showspam", "1");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
queryCollection.Add("search", searchString);
|
||||
}
|
||||
|
||||
var cats = MapTorznabCapsToTrackers(query);
|
||||
string cat = "0";
|
||||
if (cats.Count == 1)
|
||||
{
|
||||
cat = cats[0];
|
||||
}
|
||||
queryCollection.Add("cat", cat);
|
||||
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
try
|
||||
{
|
||||
CQ dom = results.Content;
|
||||
var rows = dom["table[cellpadding=2] > tbody > tr:has(td.row3)"];
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 48 * 60 * 60;
|
||||
|
||||
var qRow = row.Cq();
|
||||
var qCatLink = qRow.Find("a[href^=browse.php?cat=]").First();
|
||||
var qDetailsLink = qRow.Find("a[href^=details.php?id=]").First();
|
||||
var qSeeders = qRow.Find("td:eq(9)");
|
||||
var qLeechers = qRow.Find("td:eq(10)");
|
||||
var qDownloadLink = qRow.Find("a[href^=download.php]").First();
|
||||
var qTimeAgo = qRow.Find("td:eq(5)");
|
||||
var qSize = qRow.Find("td:eq(7)");
|
||||
|
||||
var catStr = qCatLink.Attr("href").Split('=')[1].Split('&')[0];
|
||||
release.Category = MapTrackerCatToNewznab(catStr);
|
||||
|
||||
release.Link = new Uri(SiteLink + qDownloadLink.Attr("href"));
|
||||
release.Title = qDetailsLink.Attr("title").Trim();
|
||||
release.Comments = new Uri(SiteLink + qDetailsLink.Attr("href"));
|
||||
release.Guid = release.Link;
|
||||
|
||||
var sizeStr = qSize.Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(qSeeders.Text());
|
||||
release.Peers = ParseUtil.CoerceInt(qLeechers.Text()) + release.Seeders;
|
||||
|
||||
var dateStr = qTimeAgo.Text();
|
||||
release.PublishDate = DateTimeUtil.FromTimeAgo(dateStr);
|
||||
|
||||
var files = qRow.Find("td:nth-child(4)").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(9)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
var ka = qRow.Next();
|
||||
var DLFactor = ka.Find("table > tbody > tr:nth-child(3) > td:nth-child(2)").Text().Replace("X", "");
|
||||
var ULFactor = ka.Find("table > tbody > tr:nth-child(3) > td:nth-child(1)").Text().Replace("X", "");
|
||||
release.DownloadVolumeFactor = ParseUtil.CoerceDouble(DLFactor);
|
||||
release.UploadVolumeFactor = ParseUtil.CoerceDouble(ULFactor);
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
306
src/Jackett/Indexers/Fuzer.cs
Normal file
@@ -0,0 +1,306 @@
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
using System.Threading;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class Fuzer : BaseIndexer, IIndexer
|
||||
{
|
||||
private string SearchUrl { get { return SiteLink + "index.php?name=torrents&"; } }
|
||||
private string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
private const int MAXPAGES = 3;
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public Fuzer(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps)
|
||||
: base(name: "Fuzer",
|
||||
description: "Fuzer is a private torrent website with israeli torrents.",
|
||||
link: "https://fuzer.me/",
|
||||
manager: i,
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
TorznabCaps.Categories.Clear();
|
||||
|
||||
AddMultiCategoryMapping(TorznabCatType.Movies, 7, 9, 58, 59, 60, 61, 83);
|
||||
AddMultiCategoryMapping(TorznabCatType.MoviesSD, 7, 58);
|
||||
AddMultiCategoryMapping(TorznabCatType.MoviesHD, 9, 59, 61);
|
||||
AddMultiCategoryMapping(TorznabCatType.MoviesBluRay, 59);
|
||||
AddMultiCategoryMapping(TorznabCatType.MoviesForeign, 83);
|
||||
AddMultiCategoryMapping(TorznabCatType.MoviesDVD, 58);
|
||||
AddMultiCategoryMapping(TorznabCatType.Movies3D, 9);
|
||||
AddMultiCategoryMapping(TorznabCatType.MoviesWEBDL, 9);
|
||||
AddMultiCategoryMapping(TorznabCatType.TV, 8, 10, 62, 63, 84);
|
||||
AddMultiCategoryMapping(TorznabCatType.TVHD, 10, 63);
|
||||
AddMultiCategoryMapping(TorznabCatType.TVFOREIGN, 62, 84);
|
||||
AddMultiCategoryMapping(TorznabCatType.TVSport, 64);
|
||||
AddMultiCategoryMapping(TorznabCatType.TVAnime, 65);
|
||||
AddMultiCategoryMapping(TorznabCatType.TVWEBDL, 10, 63);
|
||||
AddMultiCategoryMapping(TorznabCatType.TVSD, 8, 62, 84);
|
||||
AddMultiCategoryMapping(TorznabCatType.TVDocumentary, 8, 10, 62, 63);
|
||||
AddMultiCategoryMapping(TorznabCatType.Console, 12, 55, 56, 57);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsoleXbox, 55);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsoleXbox360, 55);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsoleXBOX360DLC, 55);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsolePS3, 12);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsolePS4, 12);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsoleXboxOne, 55);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsolePS4, 12);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsoleWii, 56);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsoleWiiwareVC, 56);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsolePSP, 57);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsoleNDS, 57);
|
||||
AddMultiCategoryMapping(TorznabCatType.MoviesOther, 57);
|
||||
AddMultiCategoryMapping(TorznabCatType.PC, 11, 15);
|
||||
AddMultiCategoryMapping(TorznabCatType.PCGames, 11);
|
||||
AddMultiCategoryMapping(TorznabCatType.PCMac, 71);
|
||||
AddMultiCategoryMapping(TorznabCatType.PCPhoneAndroid, 13);
|
||||
AddMultiCategoryMapping(TorznabCatType.PCPhoneIOS, 70);
|
||||
AddMultiCategoryMapping(TorznabCatType.Audio, 14, 66, 67, 68);
|
||||
AddMultiCategoryMapping(TorznabCatType.AudioForeign, 14);
|
||||
AddMultiCategoryMapping(TorznabCatType.AudioLossless, 67);
|
||||
AddMultiCategoryMapping(TorznabCatType.AudioAudiobook, 69);
|
||||
AddMultiCategoryMapping(TorznabCatType.AudioOther, 68);
|
||||
AddMultiCategoryMapping(TorznabCatType.Other, 17);
|
||||
AddMultiCategoryMapping(TorznabCatType.XXX, 16);
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var loginPage = await RequestStringWithCookies(LoginUrl, string.Empty);
|
||||
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "vb_login_username", configData.Username.Value },
|
||||
{ "vb_login_password", "" },
|
||||
{ "securitytoken", "guest" },
|
||||
{ "do","login"},
|
||||
{ "vb_login_md5password", StringUtil.Hash(configData.Password.Value).ToLower()},
|
||||
{ "vb_login_md5password_utf", StringUtil.Hash(configData.Password.Value).ToLower()},
|
||||
{ "cookieuser", "1" }
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, loginPage.Cookies, true, null, LoginUrl);
|
||||
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("images/loading.gif"), () =>
|
||||
{
|
||||
var errorMessage = "Couldn't login";
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
Thread.Sleep(2);
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var results = await performRegularQuery(query);
|
||||
if (results.Count() == 0)
|
||||
{
|
||||
return await performHebrewQuery(query);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<ReleaseInfo>> performHebrewQuery(TorznabQuery query)
|
||||
{
|
||||
var name = await getHebName(query.SearchTerm);
|
||||
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
return new List<ReleaseInfo>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return await performRegularQuery(query, name);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<ReleaseInfo>> performRegularQuery(TorznabQuery query, string hebName = null)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchurls = new List<string>();
|
||||
var searchUrl = SearchUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
var searchString = query.GetQueryString();
|
||||
|
||||
if (hebName != null)
|
||||
{
|
||||
searchString = hebName + " - עונה " + query.Season + " פרק " + query.Episode;
|
||||
}
|
||||
|
||||
int categoryCounter = 1;
|
||||
foreach (var cat in MapTorznabCapsToTrackers(query))
|
||||
{
|
||||
searchUrl += "c" + categoryCounter.ToString() + "=" + cat + "&";
|
||||
categoryCounter++;
|
||||
}
|
||||
|
||||
|
||||
if (string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
searchUrl = SiteLink + "index.php?name=torrents";
|
||||
}
|
||||
else
|
||||
{
|
||||
var strEncoded = HttpUtility.UrlEncode(searchString, Encoding.GetEncoding("Windows-1255"));
|
||||
searchUrl += "text=" + strEncoded + "&category=0&search=1";
|
||||
}
|
||||
|
||||
var data = await RequestBytesWithCookiesAndRetry(searchUrl);
|
||||
var results = Encoding.GetEncoding("Windows-1255").GetString(data.Content);
|
||||
try
|
||||
{
|
||||
CQ dom = results;
|
||||
ReleaseInfo release;
|
||||
|
||||
int rowCount = 0;
|
||||
var rows = dom["#collapseobj_module_17 > tr"];
|
||||
foreach (var row in rows)
|
||||
{
|
||||
CQ qRow = row.Cq();
|
||||
if (rowCount < 1 || qRow.Children().Count() != 9) //skip 1 row because there's an empty row
|
||||
{
|
||||
rowCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
release = new ReleaseInfo();
|
||||
release.Description = qRow.Find("td:nth-child(2) > a").Text(); ;
|
||||
|
||||
if (hebName != null)
|
||||
{
|
||||
release.Title = query.SearchTerm + " " + release.Description.Substring(release.Description.IndexOf(string.Format("S{0:D2}E{1:D2}", query.Season, int.Parse(query.Episode))));
|
||||
}
|
||||
else
|
||||
{
|
||||
const string DELIMITER = " | ";
|
||||
release.Title = release.Description.Substring(release.Description.IndexOf(DELIMITER) + DELIMITER.Length);
|
||||
}
|
||||
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800;
|
||||
|
||||
int seeders, peers;
|
||||
if (ParseUtil.TryCoerceInt(qRow.Find("td:nth-child(7) > div").Text(), out seeders))
|
||||
{
|
||||
release.Seeders = seeders;
|
||||
if (ParseUtil.TryCoerceInt(qRow.Find("td:nth-child(8) > div").Text(), out peers))
|
||||
{
|
||||
release.Peers = peers + release.Seeders;
|
||||
}
|
||||
}
|
||||
|
||||
string fullSize = qRow.Find("td:nth-child(5) > div").Text();
|
||||
release.Size = ReleaseInfo.GetBytes(fullSize);
|
||||
|
||||
release.Guid = new Uri(qRow.Find("td:nth-child(2) > a").Attr("href"));
|
||||
release.Link = new Uri(SiteLink + qRow.Find("td:nth-child(3) > a").Attr("href"));
|
||||
release.Comments = release.Guid;
|
||||
|
||||
string[] dateSplit = qRow.Find("td:nth-child(2) > span.torrentstime").Text().Split(' ');
|
||||
string dateString = dateSplit[1] + " " + dateSplit[3];
|
||||
release.PublishDate = DateTime.ParseExact(dateString, "dd-MM-yy HH:mm", CultureInfo.InvariantCulture);
|
||||
|
||||
string category = qRow.Find("script:nth-child(1)").Text();
|
||||
int index = category.IndexOf("category=");
|
||||
if (index == -1)
|
||||
{
|
||||
/// Other type
|
||||
category = "17";
|
||||
}
|
||||
else
|
||||
{
|
||||
category = category.Substring(index + "category=".Length, 2);
|
||||
if (category[1] == '\\')
|
||||
{
|
||||
category = category[0].ToString();
|
||||
}
|
||||
}
|
||||
|
||||
release.Category = MapTrackerCatToNewznab(category);
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(6)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (qRow.Find("img[src=\"/images/FL.png\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
private async Task<string> getHebName(string searchTerm)
|
||||
{
|
||||
const string site = "http://thetvdb.com";
|
||||
var url = site + "/index.php?searchseriesid=&tab=listseries&function=Search&";
|
||||
url += "string=" + searchTerm; // eretz + nehedert
|
||||
|
||||
|
||||
var results = await RequestStringWithCookies(url);
|
||||
|
||||
CQ dom = results.Content;
|
||||
|
||||
int rowCount = 0;
|
||||
var rows = dom["#listtable > tbody > tr"];
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
if (rowCount < 1)
|
||||
{
|
||||
rowCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
CQ qRow = row.Cq();
|
||||
CQ link = qRow.Find("td:nth-child(1) > a");
|
||||
if (link.Text().Trim().ToLower() == searchTerm.Trim().ToLower())
|
||||
{
|
||||
var address = link.Attr("href");
|
||||
if (string.IsNullOrEmpty(address)) { continue; }
|
||||
|
||||
var realAddress = site + address.Replace("lid=7", "lid=24");
|
||||
var realData = await RequestStringWithCookies(realAddress);
|
||||
|
||||
CQ realDom = realData.Content;
|
||||
return realDom["#content:nth-child(1) > h1"].Text();
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
@@ -63,16 +63,19 @@ namespace Jackett.Indexers
|
||||
{
|
||||
var loginPage = await RequestStringWithCookies(StartPageUrl, string.Empty);
|
||||
CQ cq = loginPage.Content;
|
||||
var result = new ConfigurationDataRecaptchaLogin();
|
||||
var result = this.configData;
|
||||
CQ recaptcha = cq.Find(".g-recaptcha").Attr("data-sitekey");
|
||||
if(recaptcha.Length != 0) // recaptcha not always present in login form, perhaps based on cloudflare uid or just phase of the moon
|
||||
{
|
||||
result.CookieHeader.Value = loginPage.Cookies;
|
||||
result.Captcha.SiteKey = cq.Find(".g-recaptcha").Attr("data-sitekey");
|
||||
result.Captcha.Version = "2";
|
||||
return result;
|
||||
} else
|
||||
{
|
||||
var stdResult = new ConfigurationDataBasicLogin();
|
||||
stdResult.Username.Value = configData.Username.Value;
|
||||
stdResult.Password.Value = configData.Password.Value;
|
||||
stdResult.CookieHeader.Value = loginPage.Cookies;
|
||||
return stdResult;
|
||||
}
|
||||
|
179
src/Jackett/Indexers/GhostCity.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class GhostCity : BaseIndexer, IIndexer
|
||||
{
|
||||
string LoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
string BrowsePage { get { return SiteLink + "browse.php"; } }
|
||||
|
||||
new ConfigurationDataBasicLoginWithRSSAndDisplay configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLoginWithRSSAndDisplay)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public GhostCity(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "Ghost City",
|
||||
description: "A German general tracker",
|
||||
link: "http://ghostcity.dyndns.info/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLoginWithRSSAndDisplay())
|
||||
{
|
||||
this.configData.DisplayText.Value = "Only the results from the first search result page are shown, adjust your profile settings to show the maximum.";
|
||||
this.configData.DisplayText.Name = "Notice";
|
||||
AddMultiCategoryMapping(TorznabCatType.TVAnime, 8, 34, 35, 36);
|
||||
AddMultiCategoryMapping(TorznabCatType.TVDocumentary, 12, 44, 106, 45, 46, 47);
|
||||
AddMultiCategoryMapping(TorznabCatType.Console, 92, 93, 95, 96, 97);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsoleNDS, 92);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsolePS3, 95);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsolePS4, 95);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsolePS4, 95);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsolePSP, 95);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsoleXbox, 97);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsoleXbox360, 97);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsoleXBOX360DLC, 97);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsoleXboxOne, 97);
|
||||
AddMultiCategoryMapping(TorznabCatType.ConsoleWii, 96);
|
||||
AddMultiCategoryMapping(TorznabCatType.PC, 20, 94, 40);
|
||||
AddMultiCategoryMapping(TorznabCatType.PCGames, 94);
|
||||
AddMultiCategoryMapping(TorznabCatType.PCMac, 39);
|
||||
AddMultiCategoryMapping(TorznabCatType.PCPhoneOther, 37, 38);
|
||||
AddMultiCategoryMapping(TorznabCatType.TVSport, 22, 98, 99, 100, 101);
|
||||
AddMultiCategoryMapping(TorznabCatType.Movies, 68, 69, 70, 102, 104, 103, 72, 71, 73, 74, 75, 77, 78, 79);
|
||||
AddMultiCategoryMapping(TorznabCatType.MoviesSD, 68, 69, 102, 104, 103, 72, 71, 73, 74);
|
||||
AddMultiCategoryMapping(TorznabCatType.MoviesHD, 75, 76, 77, 78, 79);
|
||||
AddMultiCategoryMapping(TorznabCatType.MoviesOther, 73);
|
||||
AddMultiCategoryMapping(TorznabCatType.MoviesBluRay, 70);
|
||||
AddMultiCategoryMapping(TorznabCatType.MoviesDVD, 102, 104, 103, 72, 71);
|
||||
AddMultiCategoryMapping(TorznabCatType.Movies3D, 69);
|
||||
AddMultiCategoryMapping(TorznabCatType.AudioVideo, 109);
|
||||
AddMultiCategoryMapping(TorznabCatType.TV, 8, 34, 35, 36, 23, 90, 88, 107, 89);
|
||||
AddMultiCategoryMapping(TorznabCatType.TVHD, 107);
|
||||
AddMultiCategoryMapping(TorznabCatType.TVSD, 89);
|
||||
AddMultiCategoryMapping(TorznabCatType.XXX, 25);
|
||||
AddMultiCategoryMapping(TorznabCatType.TVDocumentary, 88);
|
||||
AddMultiCategoryMapping(TorznabCatType.AudioAudiobook, 84);
|
||||
AddMultiCategoryMapping(TorznabCatType.BooksEbook, 83);
|
||||
AddMultiCategoryMapping(TorznabCatType.BooksMagazines, 85);
|
||||
AddMultiCategoryMapping(TorznabCatType.BooksOther, 108);
|
||||
AddMultiCategoryMapping(TorznabCatType.Other, 3, 93, 24);
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value }
|
||||
};
|
||||
|
||||
var result1 = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, SiteLink);
|
||||
CQ result1Dom = result1.Content;
|
||||
var link = result1Dom[".trow2 a"].First();
|
||||
var result2 = await RequestStringWithCookies(link.Attr("href"), result1.Cookies);
|
||||
CQ result2Dom = result2.Content;
|
||||
|
||||
await ConfigureIfOK(result1.Cookies, result2.Content.Contains("/logout.php"), () =>
|
||||
{
|
||||
var errorMessage = "Login failed.";
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = BrowsePage;
|
||||
var queryCollection = new NameValueCollection();
|
||||
|
||||
queryCollection.Add("do", "search");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
queryCollection.Add("keywords", searchString);
|
||||
}
|
||||
|
||||
queryCollection.Add("search_type", "t_name");
|
||||
|
||||
// FIXME: Tracker doesn't support multi category search
|
||||
foreach (var cat in MapTorznabCapsToTrackers(query))
|
||||
{
|
||||
queryCollection.Add("category", cat);
|
||||
}
|
||||
|
||||
if (queryCollection.Count > 0)
|
||||
{
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
}
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
|
||||
try
|
||||
{
|
||||
CQ dom = results.Content;
|
||||
|
||||
var rows = dom["#sortabletable tr"];
|
||||
foreach (var row in rows.Skip(1))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
var qRow = row.Cq();
|
||||
release.Title = qRow.Find(".tooltip-content div").First().Text();
|
||||
if (string.IsNullOrWhiteSpace(release.Title))
|
||||
continue;
|
||||
release.Description = qRow.Find(".tooltip-content div").Get(1).InnerText.Trim();
|
||||
|
||||
var qLink = row.Cq().Find("td:eq(2) a:eq(0)");
|
||||
release.Link = new Uri(qLink.Attr("href"));
|
||||
release.Guid = release.Link;
|
||||
release.Comments = new Uri(qRow.Find(".tooltip-target a").First().Attr("href"));
|
||||
|
||||
var dateString = qRow.Find("td:eq(1) div").Last().Children().Remove().End().Text().Trim();
|
||||
release.PublishDate = DateTime.ParseExact(dateString, "dd-MM-yyyy HH:mm", CultureInfo.InvariantCulture);
|
||||
|
||||
var sizeStr = qRow.Find("td:eq(4)").Text().Trim();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(qRow.Find("td:eq(6)").Text().Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(qRow.Find("td:eq(7)").Text().Trim()) + release.Seeders;
|
||||
|
||||
var catLink = row.Cq().Find("td:eq(0) a").First().Attr("href");
|
||||
var catSplit = catLink.IndexOf("category=");
|
||||
if (catSplit > -1)
|
||||
{
|
||||
catLink = catLink.Substring(catSplit + 9);
|
||||
}
|
||||
|
||||
release.Category = MapTrackerCatToNewznab(catLink);
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,13 +14,14 @@ using System.Web;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Globalization;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class HDSpace : BaseIndexer, IIndexer
|
||||
{
|
||||
private string LoginUrl { get { return SiteLink + "index.php?page=login"; } }
|
||||
private string SearchUrl { get { return SiteLink + "index.php?page=torrents&active=0&options=0&category=21%3B22&search={0}"; } }
|
||||
private string SearchUrl { get { return SiteLink + "index.php?page=torrents&"; } }
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
@@ -39,7 +40,36 @@ namespace Jackett.Indexers
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
}
|
||||
AddCategoryMapping(15, TorznabCatType.MoviesBluRay); // Movie / Blu-ray
|
||||
AddMultiCategoryMapping(TorznabCatType.MoviesHD,
|
||||
19, // Movie / 1080p
|
||||
41, // Movie / 4K UHD
|
||||
18, // Movie / 720p
|
||||
40, // Movie / Remux
|
||||
16 // Movie / HD-DVD
|
||||
);
|
||||
|
||||
AddMultiCategoryMapping(TorznabCatType.TVHD,
|
||||
21, // TV Show / 720p HDTV
|
||||
22 // TV Show / 1080p HDTV
|
||||
);
|
||||
|
||||
AddCategoryMapping(30, TorznabCatType.AudioLossless); // Music / Lossless
|
||||
AddCategoryMapping(31, TorznabCatType.AudioVideo); // Music / Videos
|
||||
|
||||
AddMultiCategoryMapping(TorznabCatType.TVDocumentary,
|
||||
24, // TV Show / Documentary / 720p
|
||||
25 // TV Show / Documentary / 1080p
|
||||
);
|
||||
|
||||
AddMultiCategoryMapping(TorznabCatType.XXX,
|
||||
33, // XXX / 720p
|
||||
34 // XXX / 1080p
|
||||
);
|
||||
|
||||
AddCategoryMapping("37", TorznabCatType.PC);
|
||||
AddCategoryMapping("38", TorznabCatType.Other);
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
@@ -69,8 +99,22 @@ namespace Jackett.Indexers
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var episodeSearchUrl = string.Format(SearchUrl, HttpUtility.UrlEncode(query.GetQueryString()));
|
||||
var response = await RequestStringWithCookiesAndRetry(episodeSearchUrl);
|
||||
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = SearchUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
queryCollection.Add("active", "0");
|
||||
queryCollection.Add("options", "0");
|
||||
queryCollection.Add("category", string.Join(";", MapTorznabCapsToTrackers(query)));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
queryCollection.Add("search", searchString);
|
||||
}
|
||||
|
||||
searchUrl += queryCollection.GetQueryString();
|
||||
|
||||
var response = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
var results = response.Content;
|
||||
|
||||
try
|
||||
@@ -112,6 +156,25 @@ namespace Jackett.Indexers
|
||||
release.Seeders = ParseUtil.CoerceInt(row.ChildElements.ElementAt(7).Cq().Text());
|
||||
release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(8).Cq().Text()) + release.Seeders;
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(10)").Text();
|
||||
grabs = grabs.Replace("---", "0");
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (qRow.Find("img[title=\"FreeLeech\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else if (qRow.Find("img[src=\"images/sf.png\"]").Length >= 1) // side freeleech
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else if (qRow.Find("img[title=\"Half FreeLeech\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0.5;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
var qCat = qRow.Find("a[href^=\"index.php?page=torrents&category=\"]");
|
||||
var cat = qCat.Attr("href").Split('=')[2];
|
||||
release.Category = MapTrackerCatToNewznab(cat);
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
169
src/Jackett/Indexers/Hebits.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class Hebits : BaseIndexer, IIndexer
|
||||
{
|
||||
private string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
private string LoginPostUrl { get { return SiteLink + "takeloginAjax.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "browse.php?sort=4&type=desc"; } }
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public Hebits(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "Hebits",
|
||||
description: "The Israeli Tracker",
|
||||
link: "https://hebits.net/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
downloadBase: "https://hebits.net/",
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
|
||||
AddCategoryMapping(19, TorznabCatType.MoviesSD);
|
||||
AddCategoryMapping(25, TorznabCatType.MoviesOther); // Israeli Content
|
||||
AddCategoryMapping(20, TorznabCatType.MoviesDVD);
|
||||
AddCategoryMapping(36, TorznabCatType.MoviesBluRay);
|
||||
AddCategoryMapping(27, TorznabCatType.MoviesHD);
|
||||
|
||||
AddCategoryMapping(7, TorznabCatType.TVSD); // Israeli SDTV
|
||||
AddCategoryMapping(24, TorznabCatType.TVSD); // English SDTV
|
||||
AddCategoryMapping(1, TorznabCatType.TVHD); // Israel HDTV
|
||||
AddCategoryMapping(37, TorznabCatType.TVHD); // Israel HDTV
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value }
|
||||
};
|
||||
|
||||
// Get inital cookies
|
||||
CookieHeader = string.Empty;
|
||||
var result = await RequestLoginAndFollowRedirect(LoginPostUrl, pairs, CookieHeader, true, null, SiteLink);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("OK"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var messageEl = dom["#errorMsg"].Last();
|
||||
var errorMessage = messageEl.Text().Trim();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = SearchUrl;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
searchUrl += "&search=" + HttpUtility.UrlEncode(searchString);
|
||||
}
|
||||
string.Format(SearchUrl, HttpUtility.UrlEncode(searchString));
|
||||
|
||||
var cats = MapTorznabCapsToTrackers(query);
|
||||
if (cats.Count > 0)
|
||||
{
|
||||
foreach (var cat in cats)
|
||||
{
|
||||
searchUrl += "&c" + cat + "=1";
|
||||
}
|
||||
}
|
||||
|
||||
var response = await RequestBytesWithCookies(searchUrl);
|
||||
var results = Encoding.GetEncoding("windows-1255").GetString(response.Content);
|
||||
try
|
||||
{
|
||||
CQ dom = results;
|
||||
|
||||
CQ qRows = dom[".browse > div > div"];
|
||||
|
||||
foreach (var row in qRows)
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
|
||||
var qRow = row.Cq();
|
||||
|
||||
var debug = qRow.Html();
|
||||
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800;
|
||||
|
||||
var titleParts = qRow.Find(".bTitle").Text().Split('/');
|
||||
if (titleParts.Length >= 2)
|
||||
release.Title = titleParts[1].Trim();
|
||||
else
|
||||
release.Title = titleParts[0].Trim();
|
||||
|
||||
release.Link = new Uri(SiteLink + qRow.Find("a").Attr("href"));
|
||||
release.Guid = release.Link;
|
||||
|
||||
var dateString = qRow.Find("div:last-child").Text().Trim();
|
||||
var pattern = "\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}";
|
||||
var match = Regex.Match(dateString, pattern);
|
||||
if (match.Success)
|
||||
{
|
||||
release.PublishDate = DateTime.ParseExact(match.Value, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
var sizeStr = qRow.Find(".bSize").Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
release.Seeders = ParseUtil.CoerceInt(qRow.Find(".bUping").Text().Trim());
|
||||
release.Peers = release.Seeders + ParseUtil.CoerceInt(qRow.Find(".bDowning").Text().Trim());
|
||||
|
||||
var files = qRow.Find("div.bFiles").Get(0).LastChild.ToString();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = qRow.Find("div.bFinish").Get(0).LastChild.ToString();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (qRow.Find("img[src=\"/pic/free.jpg\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
if (qRow.Find("img[src=\"/pic/triple.jpg\"]").Length >= 1)
|
||||
release.UploadVolumeFactor = 3;
|
||||
else if (qRow.Find("img[src=\"/pic/double.jpg\"]").Length >= 1)
|
||||
release.UploadVolumeFactor = 2;
|
||||
else
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
@@ -131,9 +131,19 @@ namespace Jackett.Indexers
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(row.ChildElements.ElementAt(6).Cq().Text());
|
||||
release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(7).Cq().Text()) + release.Seeders;
|
||||
release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(7).Cq().Text()) + release.Seeders;
|
||||
|
||||
var files = row.Cq().Find("td:nth-child(4)").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
releases.Add(release);
|
||||
if (row.Cq().Find("img[src=\"/static//common/browse/freeleech.png\"]").Any())
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
220
src/Jackett/Indexers/HouseOfTorrents.cs
Normal file
@@ -0,0 +1,220 @@
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class HouseOfTorrents : BaseIndexer, IIndexer
|
||||
{
|
||||
private string SearchUrl { get { return SiteLink + "browse.php"; } }
|
||||
private string LoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
private string CaptchaUrl { get { return SiteLink + "simpleCaptcha.php?numImages=1"; } }
|
||||
|
||||
new ConfigurationDataBasicLoginWithRSSAndDisplay configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLoginWithRSSAndDisplay)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public HouseOfTorrents(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps)
|
||||
: base(name: "House-of-Torrents",
|
||||
description: "A general tracker",
|
||||
link: "https://houseoftorrents.me/",
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLoginWithRSSAndDisplay())
|
||||
{
|
||||
AddCategoryMapping(42, TorznabCatType.PCMac); // Applications/Mac
|
||||
AddCategoryMapping(34, TorznabCatType.PC); // Applications/PC
|
||||
AddCategoryMapping(66, TorznabCatType.MoviesForeign); // Foreign
|
||||
AddCategoryMapping(38, TorznabCatType.MoviesForeign); // Foreign/French
|
||||
AddCategoryMapping(39, TorznabCatType.MoviesForeign); // Foreign/German
|
||||
AddCategoryMapping(40, TorznabCatType.MoviesForeign); // Foreign/Spanish
|
||||
AddCategoryMapping(41, TorznabCatType.MoviesForeign); // Foreign/Swedish
|
||||
AddCategoryMapping(67, TorznabCatType.ConsoleNDS); // Games/Nintendo
|
||||
AddCategoryMapping(9 , TorznabCatType.PCGames); // Games/PC
|
||||
AddCategoryMapping(8, TorznabCatType.ConsolePS3); // Games/PS3
|
||||
AddCategoryMapping(30, TorznabCatType.ConsolePS4); // Games/PS4
|
||||
AddCategoryMapping(7, TorznabCatType.ConsolePSP); // Games/PSP
|
||||
AddCategoryMapping(29, TorznabCatType.ConsoleWii); // Games/Wii
|
||||
AddCategoryMapping(31, TorznabCatType.ConsoleXbox360); // Games/XBOX360
|
||||
AddCategoryMapping(32, TorznabCatType.ConsoleXboxOne); // Games/XBOXONE
|
||||
AddCategoryMapping(71, TorznabCatType.PCPhoneAndroid); // Mobile/Android
|
||||
AddCategoryMapping(72, TorznabCatType.PCPhoneIOS); // Mobile/iOS
|
||||
AddCategoryMapping(47, TorznabCatType.Movies3D); // Movies/3D
|
||||
AddCategoryMapping(43, TorznabCatType.MoviesBluRay); // Movies/Bluray
|
||||
AddCategoryMapping(84, TorznabCatType.MoviesSD); // Movies/Cam
|
||||
AddCategoryMapping(44, TorznabCatType.MoviesDVD); // Movies/DVD-R
|
||||
AddCategoryMapping(45, TorznabCatType.Movies); // Movies/MP4
|
||||
AddCategoryMapping(69, TorznabCatType.Movies); // Movies/Packs
|
||||
AddCategoryMapping(46, TorznabCatType.MoviesSD); // Movies/SD
|
||||
AddCategoryMapping(11, TorznabCatType.MoviesHD); // Movies/x264
|
||||
AddCategoryMapping(83, TorznabCatType.MoviesHD); // Movies/x265
|
||||
AddCategoryMapping(10, TorznabCatType.MoviesOther); // Movies/XviD
|
||||
AddCategoryMapping(36, TorznabCatType.AudioLossless); // Music/FLAC
|
||||
AddCategoryMapping(12, TorznabCatType.AudioMP3); // Music/MP3
|
||||
AddCategoryMapping(79, TorznabCatType.Audio); // Music/Pack
|
||||
AddCategoryMapping(28, TorznabCatType.AudioVideo); // Music/Video
|
||||
AddCategoryMapping(49, TorznabCatType.TVAnime); // Others/Anime
|
||||
AddCategoryMapping(80, TorznabCatType.AudioAudiobook); // Others/AudioBook
|
||||
AddCategoryMapping(60, TorznabCatType.Other); // Others/Boxsets
|
||||
AddCategoryMapping(65, TorznabCatType.TVDocumentary); // Others/Documentary
|
||||
AddCategoryMapping(61, TorznabCatType.Books); // Others/E-Book
|
||||
AddCategoryMapping(51, TorznabCatType.Other); // Others/RARFIX
|
||||
AddCategoryMapping(74, TorznabCatType.TVSport); // Sports
|
||||
AddCategoryMapping(75, TorznabCatType.TVSport); // Sports/Boxing
|
||||
AddCategoryMapping(76, TorznabCatType.TVSport); // Sports/Racing
|
||||
AddCategoryMapping(77, TorznabCatType.TVSport); // Sports/UFC
|
||||
AddCategoryMapping(78, TorznabCatType.TVSport); // Sports/WWE
|
||||
AddCategoryMapping(68, TorznabCatType.TV); // TV/Packs
|
||||
AddCategoryMapping(53, TorznabCatType.TVSD); // TV/SD
|
||||
AddCategoryMapping(54, TorznabCatType.TVHD); // TV/x264
|
||||
AddCategoryMapping(82, TorznabCatType.TVHD); // TV/x265
|
||||
AddCategoryMapping(55, TorznabCatType.TVOTHER); // Tv/XviD
|
||||
AddCategoryMapping(63, TorznabCatType.XXX); // XXX
|
||||
AddCategoryMapping(57, TorznabCatType.XXX); // XXX/0-DAY
|
||||
AddCategoryMapping(58, TorznabCatType.XXXImageset); // XXX/IMAGESET
|
||||
AddCategoryMapping(81, TorznabCatType.XXXPacks); // XXX/Pack
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
var result1 = await RequestStringWithCookies(CaptchaUrl);
|
||||
var json1 = JObject.Parse(result1.Content);
|
||||
var captchaSelection = json1["images"][0]["hash"];
|
||||
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "captchaSelection", (string)captchaSelection },
|
||||
{ "submitme", "X" }
|
||||
};
|
||||
|
||||
var result2 = await RequestLoginAndFollowRedirect(LoginUrl, pairs, result1.Cookies, true, null, null, true);
|
||||
|
||||
await ConfigureIfOK(result2.Cookies, result2.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
var errorMessage = result2.Content;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
List<ReleaseInfo> releases = new List<ReleaseInfo>();
|
||||
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = SearchUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
queryCollection.Add("searchin", "title");
|
||||
queryCollection.Add("incldead", "1");
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
// use AND+wildcard operator to avoid getting to many useless results
|
||||
var searchStringArray = Regex.Split(searchString.Trim(), "[ _.-]+", RegexOptions.Compiled).ToList();
|
||||
searchStringArray = searchStringArray.Where(x => x.Length >= 3).ToList(); // remove words with less than 3 characters
|
||||
searchStringArray = searchStringArray.Select(x => "+" + x).ToList(); // add AND operators+wildcards
|
||||
var searchStringFinal = String.Join("", searchStringArray);
|
||||
queryCollection.Add("search", searchStringFinal);
|
||||
}
|
||||
|
||||
foreach (var cat in MapTorznabCapsToTrackers(query))
|
||||
{
|
||||
queryCollection.Add("c" + cat, "1");
|
||||
}
|
||||
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
try
|
||||
{
|
||||
CQ dom = results.Content;
|
||||
var rows = dom["table.tt > tbody > tr"];
|
||||
foreach (var row in rows.Skip(1))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 72 * 60 * 60;
|
||||
|
||||
var qRow = row.Cq();
|
||||
|
||||
var qDetailsLink = qRow.Find("a[href^=details.php?id=]").First();
|
||||
release.Title = qDetailsLink.Text().Trim();
|
||||
|
||||
// HoT search returns should support AND search but it simply doesn't work, so we AND filter it manualy
|
||||
if (!query.MatchQueryStringAND(release.Title))
|
||||
continue;
|
||||
|
||||
var qCatLink = qRow.Find("a[href^=browse.php?cat=]").First();
|
||||
var qSeeders = qRow.Find("td:eq(8)");
|
||||
var qLeechers = qRow.Find("td:eq(9)");
|
||||
var qDownloadLink = qRow.Find("a[href^=download.php]").First();
|
||||
var qTimeAgo = qRow.Find("td:eq(5)");
|
||||
var qSize = qRow.Find("td:eq(6)");
|
||||
|
||||
var catStr = qCatLink.Attr("href").Split('=')[1];
|
||||
release.Category = MapTrackerCatToNewznab(catStr);
|
||||
|
||||
release.Link = new Uri(SiteLink + qDownloadLink.Attr("href"));
|
||||
release.Comments = new Uri(SiteLink + qDetailsLink.Attr("href"));
|
||||
release.Guid = release.Link;
|
||||
|
||||
var sizeStr = qSize.Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(qSeeders.Text());
|
||||
release.Peers = ParseUtil.CoerceInt(qLeechers.Text()) + release.Seeders;
|
||||
|
||||
var dateStr = qTimeAgo.Text();
|
||||
DateTime pubDateUtc;
|
||||
var Timeparts = dateStr.Split(new char[] { ' ' }, 2)[1];
|
||||
if (dateStr.StartsWith("Today "))
|
||||
pubDateUtc = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) + DateTime.ParseExact(dateStr.Split(new char[] { ' ' }, 2)[1], "hh:mm tt", System.Globalization.CultureInfo.InvariantCulture).TimeOfDay;
|
||||
else if (dateStr.StartsWith("Yesterday "))
|
||||
pubDateUtc = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) +
|
||||
DateTime.ParseExact(dateStr.Split(new char[] { ' ' }, 2)[1], "hh:mm tt", System.Globalization.CultureInfo.InvariantCulture).TimeOfDay - TimeSpan.FromDays(1);
|
||||
else
|
||||
pubDateUtc = DateTime.SpecifyKind(DateTime.ParseExact(dateStr, "MMM d yyyy hh:mm tt", CultureInfo.InvariantCulture), DateTimeKind.Unspecified);
|
||||
|
||||
release.PublishDate = pubDateUtc.ToLocalTime();
|
||||
|
||||
var files = qRow.Find("td:nth-child(4)").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(8) > a").Html();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs.Split('<')[0]);
|
||||
|
||||
release.DownloadVolumeFactor = 0; // ratioless
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
@@ -21,11 +21,14 @@ namespace Jackett.Indexers
|
||||
{
|
||||
public class IPTorrents : BaseIndexer, IIndexer
|
||||
{
|
||||
private string BrowseUrl { get { return SiteLink + "t"; } }
|
||||
private string UseLink { get { return (!String.IsNullOrEmpty(this.configData.AlternateLink.Value) ? this.configData.AlternateLink.Value : SiteLink); } }
|
||||
string TakeLoginUrl { get { return UseLink + "take_login.php"; } }
|
||||
private string BrowseUrl { get { return UseLink + "t"; } }
|
||||
private List<String> KnownURLs = new List<String> { "https://nemo.iptorrents.com/", "https://ipt.rocks/" };
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
new ConfigurationDataBasicLoginWithAlternateLink configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
get { return (ConfigurationDataBasicLoginWithAlternateLink)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
@@ -38,8 +41,12 @@ namespace Jackett.Indexers
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
configData: new ConfigurationDataBasicLoginWithAlternateLink())
|
||||
{
|
||||
this.configData.Instructions.Value = this.DisplayName + " has multiple URLs. The default (" + this.SiteLink + ") can be changed by entering a new value in the box below.";
|
||||
this.configData.Instructions.Value += "The following are some known URLs for " + this.DisplayName;
|
||||
this.configData.Instructions.Value += "<ul><li>" + String.Join("</li><li>", this.KnownURLs.ToArray()) + "</li></ul>";
|
||||
|
||||
AddCategoryMapping(72, TorznabCatType.Movies);
|
||||
AddCategoryMapping(77, TorznabCatType.MoviesSD);
|
||||
AddCategoryMapping(89, TorznabCatType.MoviesSD);
|
||||
@@ -92,9 +99,9 @@ namespace Jackett.Indexers
|
||||
};
|
||||
var request = new Utils.Clients.WebRequest()
|
||||
{
|
||||
Url = SiteLink,
|
||||
Url = TakeLoginUrl,
|
||||
Type = RequestType.POST,
|
||||
Referer = SiteLink,
|
||||
Referer = UseLink,
|
||||
PostData = pairs
|
||||
};
|
||||
var response = await webclient.GetString(request);
|
||||
@@ -141,7 +148,7 @@ namespace Jackett.Indexers
|
||||
{
|
||||
CQ dom = results;
|
||||
|
||||
var rows = dom["table.torrents > tbody > tr"];
|
||||
var rows = dom["table[id='torrents'] > tbody > tr"];
|
||||
foreach (var row in rows.Skip(1))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
@@ -156,7 +163,7 @@ namespace Jackett.Indexers
|
||||
}
|
||||
|
||||
release.Description = release.Title;
|
||||
release.Guid = new Uri(SiteLink + qTitleLink.Attr("href").Substring(1));
|
||||
release.Guid = new Uri(UseLink + qTitleLink.Attr("href").Substring(1));
|
||||
release.Comments = release.Guid;
|
||||
|
||||
var descString = qRow.Find(".t_ctime").Text();
|
||||
@@ -165,7 +172,7 @@ namespace Jackett.Indexers
|
||||
release.PublishDate = DateTimeUtil.FromTimeAgo(dateString);
|
||||
|
||||
var qLink = row.ChildElements.ElementAt(3).Cq().Children("a");
|
||||
release.Link = new Uri(SiteLink + HttpUtility.UrlEncode(qLink.Attr("href").TrimStart('/')));
|
||||
release.Link = new Uri(UseLink + HttpUtility.UrlEncode(qLink.Attr("href").TrimStart('/')));
|
||||
|
||||
var sizeStr = row.ChildElements.ElementAt(5).Cq().Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
@@ -176,6 +183,16 @@ namespace Jackett.Indexers
|
||||
var cat = row.Cq().Find("td:eq(0) a").First().Attr("href").Substring(1);
|
||||
release.Category = MapTrackerCatToNewznab(cat);
|
||||
|
||||
var grabs = row.Cq().Find("td:nth-child(7)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if(row.Cq().Find("span.t_tag_free_leech").Any())
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
@@ -74,23 +74,13 @@ namespace Jackett.Indexers
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value }
|
||||
};
|
||||
var request = new Utils.Clients.WebRequest()
|
||||
{
|
||||
Url = LoginUrl,
|
||||
Type = RequestType.POST,
|
||||
Referer = SiteLink,
|
||||
PostData = pairs
|
||||
};
|
||||
var response = await webclient.GetString(request);
|
||||
CQ splashDom = response.Content;
|
||||
var link = splashDom[".trow2 a"].First();
|
||||
var resultPage = await RequestStringWithCookies(link.Attr("href"), response.Cookies);
|
||||
CQ resultDom = resultPage.Content;
|
||||
|
||||
await ConfigureIfOK(response.Cookies, resultPage.Content.Contains("/logout.php"), () =>
|
||||
var response = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, LoginUrl);
|
||||
CQ resultDom = response.Content;
|
||||
|
||||
await ConfigureIfOK(response.Cookies, response.Content.Contains("/logout.php"), () =>
|
||||
{
|
||||
var tries = resultDom["#main tr:eq(1) td font"].First().Text();
|
||||
var errorMessage = "Incorrect username or password! " + tries + " tries remaining.";
|
||||
var errorMessage = response.Content;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
|
||||
@@ -129,7 +119,7 @@ namespace Jackett.Indexers
|
||||
release.Comments = new Uri(qRow.Find(".tooltip-target a").First().Attr("href"));
|
||||
|
||||
// 07-22-2015 11:08 AM
|
||||
var dateString = qRow.Find("td:eq(1) div").Last().Children().Remove().End().Text().Trim();
|
||||
var dateString = qRow.Find("td:eq(1) div").Last().Get(0).LastChild.ToString().Trim();
|
||||
release.PublishDate = DateTime.ParseExact(dateString, "MM-dd-yyyy hh:mm tt", CultureInfo.InvariantCulture);
|
||||
|
||||
var sizeStr = qRow.Find("td:eq(4)").Text().Trim();
|
||||
@@ -146,6 +136,19 @@ namespace Jackett.Indexers
|
||||
}
|
||||
|
||||
release.Category = MapTrackerCatToNewznab(catLink);
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(6)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (qRow.Find("img[title^=\"Free Torrent\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else if (qRow.Find("img[title^=\"Silver Torrent\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0.5;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
@@ -1,36 +1,30 @@
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Models;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using AngleSharp.Dom;
|
||||
using AngleSharp.Parser.Html;
|
||||
using CsQuery;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class MoreThanTV : BaseIndexer, IIndexer
|
||||
{
|
||||
private string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "ajax.php?action=browse&searchstr="; } }
|
||||
private string DownloadUrl { get { return SiteLink + "torrents.php?action=download&id="; } }
|
||||
private string GuidUrl { get { return SiteLink + "torrents.php?torrentid="; } }
|
||||
private string LoginUrl => SiteLink + "login.php";
|
||||
private string SearchUrl => SiteLink + "ajax.php?action=browse&searchstr=";
|
||||
private string DownloadUrl => SiteLink + "torrents.php?action=download&id=";
|
||||
private string GuidUrl => SiteLink + "torrents.php?torrentid=";
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
private ConfigurationDataBasicLogin ConfigData => (ConfigurationDataBasicLogin) configData;
|
||||
|
||||
public MoreThanTV(IIndexerManagerService i, IWebClient c, Logger l, IProtectionService ps)
|
||||
: base(name: "MoreThanTV",
|
||||
@@ -48,111 +42,237 @@ namespace Jackett.Indexers
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
ConfigData.LoadValuesFromJson(configJson);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "username", ConfigData.Username.Value },
|
||||
{ "password", ConfigData.Password.Value },
|
||||
{ "login", "Log in" },
|
||||
{ "keeplogged", "1" }
|
||||
};
|
||||
|
||||
var preRequest = await RequestStringWithCookiesAndRetry(LoginUrl, string.Empty);
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, preRequest.Cookies, true, SearchUrl, SiteLink);
|
||||
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("status\":\"success\""), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
dom["#loginform > table"].Remove();
|
||||
var errorMessage = dom["#loginform"].Text().Trim().Replace("\n\t", " ");
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
throw new ExceptionWithConfigData(errorMessage, ConfigData);
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
private void FillReleaseInfoFromJson(ReleaseInfo release, JObject r)
|
||||
{
|
||||
var id = r["torrentId"];
|
||||
release.Size = (long)r["size"];
|
||||
release.Seeders = (int)r["seeders"];
|
||||
release.Peers = (int)r["leechers"] + release.Seeders;
|
||||
release.Guid = new Uri(GuidUrl + id);
|
||||
release.Comments = release.Guid;
|
||||
|
||||
if ((string)r["category"] == "TV")
|
||||
{
|
||||
release.Category = TorznabCatType.TV.ID;
|
||||
}
|
||||
else if ((string)r["category"] == "Movies")
|
||||
{
|
||||
release.Category = TorznabCatType.Movies.ID;
|
||||
}
|
||||
|
||||
release.Link = new Uri(DownloadUrl + id);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var isTv = Array.IndexOf(query.Categories, TorznabCatType.TV.ID) > -1;
|
||||
var releases = new List<ReleaseInfo>();
|
||||
string qryString = query.GetQueryString();
|
||||
var searchQuery = query.GetQueryString();
|
||||
|
||||
Match matchQry = new Regex(@".*\s[Ss]{1}\d{2}$").Match(qryString);
|
||||
if (matchQry.Success)
|
||||
await GetReleases(releases, query, searchQuery);
|
||||
|
||||
// Search for torrent groups
|
||||
if (isTv)
|
||||
{
|
||||
//If search string ends in S## eg. S03 (season search) add an asterix to search term
|
||||
qryString += "*";
|
||||
var seasonMatch = new Regex(@".*\s[Ss]{1}\d{2}").Match(query.GetQueryString());
|
||||
if (seasonMatch.Success)
|
||||
{
|
||||
var newSearchQuery = Regex.Replace(searchQuery, @"[Ss]{1}\d{2}", $"Season {query.Season}");
|
||||
|
||||
await GetReleases(releases, query, newSearchQuery);
|
||||
}
|
||||
}
|
||||
|
||||
var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(qryString);
|
||||
WebClientStringResult response = await RequestStringWithCookiesAndRetry(episodeSearchUrl);
|
||||
return releases;
|
||||
}
|
||||
|
||||
private string GetTorrentSearchUrl(int[] categories, string searchQuery)
|
||||
{
|
||||
var extra = "";
|
||||
|
||||
if (Array.IndexOf(categories, TorznabCatType.Movies.ID) > -1)
|
||||
extra += "&filter_cat%5B1%5D=1";
|
||||
|
||||
if (Array.IndexOf(categories, TorznabCatType.TV.ID) > -1)
|
||||
extra += "&filter_cat%5B2%5D=1";
|
||||
|
||||
return SiteLink + $"torrents.php?searchstr={HttpUtility.UrlEncode(searchQuery)}&tags_type=1&order_by=time&order_way=desc&group_results=1{extra}&action=basic&searchsubmit=1";
|
||||
}
|
||||
|
||||
private async Task GetReleases(ICollection<ReleaseInfo> releases, TorznabQuery query, string searchQuery)
|
||||
{
|
||||
var searchUrl = GetTorrentSearchUrl(query.Categories, searchQuery);
|
||||
var response = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
|
||||
try
|
||||
{
|
||||
string decodedResponse = WebUtility.HtmlDecode(response.Content);
|
||||
var json = JObject.Parse(decodedResponse);
|
||||
foreach (JObject r in json["response"]["results"])
|
||||
var parser = new HtmlParser();
|
||||
var document = parser.Parse(response.Content);
|
||||
var groups = document.QuerySelectorAll(".torrent_table > tbody > tr.group");
|
||||
var torrents = document.QuerySelectorAll(".torrent_table > tbody > tr.torrent");
|
||||
|
||||
// Loop through all torrent (season) groups
|
||||
foreach (var group in groups)
|
||||
{
|
||||
DateTime pubDate = DateTime.MinValue;
|
||||
double dateNum;
|
||||
if (double.TryParse((string)r["groupTime"], out dateNum))
|
||||
var showName = group.QuerySelector(".tp-showname a").InnerHtml.Replace("(", "").Replace(")", "").Replace(' ', '.');
|
||||
var season = group.QuerySelector(".big_info a").InnerHtml;
|
||||
|
||||
// Loop through all group items
|
||||
var previousElement = group;
|
||||
var qualityEdition = string.Empty;
|
||||
while (true)
|
||||
{
|
||||
pubDate = DateTimeUtil.UnixTimestampToDateTime(dateNum);
|
||||
pubDate = DateTime.SpecifyKind(pubDate, DateTimeKind.Utc).ToLocalTime();
|
||||
}
|
||||
var groupItem = previousElement.NextElementSibling;
|
||||
|
||||
string groupName = (string)r["groupName"];
|
||||
if (groupItem == null) break;
|
||||
|
||||
if (r["torrents"] is JArray)
|
||||
{
|
||||
string showName = (string) r["artist"];
|
||||
if (!groupItem.ClassList[0].Equals("group_torrent") ||
|
||||
!groupItem.ClassList[1].StartsWith("groupid_")) break;
|
||||
|
||||
foreach (JObject t in r["torrents"])
|
||||
// Found a new edition
|
||||
if (groupItem.ClassList[2].Equals("edition"))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.PublishDate = pubDate;
|
||||
release.Title = $"{showName} {groupName}";
|
||||
release.Description = $"{showName} {groupName}";
|
||||
FillReleaseInfoFromJson(release, t);
|
||||
releases.Add(release);
|
||||
qualityEdition = groupItem.QuerySelector(".edition_info strong").TextContent.Split('/')[1].Trim();
|
||||
}
|
||||
else if (groupItem.ClassList[2].StartsWith("edition_"))
|
||||
{
|
||||
if (qualityEdition.Equals(string.Empty)) break;
|
||||
|
||||
// Parse required data
|
||||
var downloadAnchor = groupItem.QuerySelectorAll("td a").Last();
|
||||
var qualityData = downloadAnchor.InnerHtml.Split('/');
|
||||
|
||||
if (qualityData.Length < 2)
|
||||
throw new Exception($"We expected 2 or more quality datas, instead we have {qualityData.Length}.");
|
||||
|
||||
// Build title
|
||||
var title = string.Join(".", new List<string>
|
||||
{
|
||||
showName,
|
||||
SeasonToShortSeason(season),
|
||||
qualityData[1].Trim(),
|
||||
qualityEdition, // Audio quality should be after this one. Unobtainable at the moment.
|
||||
$"{qualityData[0].Trim()}-MTV"
|
||||
});
|
||||
|
||||
releases.Add(GetReleaseInfo(groupItem, downloadAnchor, title, TorznabCatType.TV.ID));
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
previousElement = groupItem;
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through all torrents
|
||||
foreach (var torrent in torrents)
|
||||
{
|
||||
// Parse required data
|
||||
var downloadAnchor = torrent.QuerySelector(".big_info > .group_info > a");
|
||||
var title = downloadAnchor.TextContent;
|
||||
|
||||
int category;
|
||||
var categories = torrent.QuerySelector(".cats_col div").ClassList;
|
||||
if (categories.Contains("cats_tv"))
|
||||
{
|
||||
category = TorznabCatType.TV.ID;
|
||||
}
|
||||
else if (categories.Contains("cats_movies"))
|
||||
{
|
||||
category = TorznabCatType.Movies.ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.PublishDate = pubDate;
|
||||
release.Title = groupName;
|
||||
release.Description = groupName;
|
||||
FillReleaseInfoFromJson(release, r);
|
||||
releases.Add(release);
|
||||
throw new Exception("Couldn't find category.");
|
||||
}
|
||||
|
||||
releases.Add(GetReleaseInfo(torrent, downloadAnchor, title, category));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(response.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
private ReleaseInfo GetReleaseInfo(IElement row, IElement downloadAnchor, string title, int category)
|
||||
{
|
||||
// Parse required data
|
||||
var downloadAnchorHref = downloadAnchor.Attributes["href"].Value;
|
||||
var torrentId = downloadAnchorHref.Substring(downloadAnchorHref.LastIndexOf('=') + 1);
|
||||
var files = int.Parse(row.QuerySelector("td:nth-child(4)").TextContent);
|
||||
var publishDate = DateTime.ParseExact(row.QuerySelector(".time.tooltip").Attributes["title"].Value, "MMM dd yyyy, HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal);
|
||||
var torrentData = row.QuerySelectorAll(".number_column"); // Size (xx.xx GB[ (Max)]) Snatches (xx) Seeders (xx) Leechers (xx)
|
||||
|
||||
if (torrentData.Length != 4)
|
||||
throw new Exception($"We expected 4 torrent datas, instead we have {torrentData.Length}.");
|
||||
|
||||
if (torrentId.Contains('#'))
|
||||
torrentId = torrentId.Split('#')[0];
|
||||
|
||||
var size = ParseSizeToBytes(torrentData[0].TextContent);
|
||||
var grabs = int.Parse(torrentData[1].TextContent);
|
||||
var seeders = int.Parse(torrentData[2].TextContent);
|
||||
var guid = new Uri(GuidUrl + torrentId);
|
||||
|
||||
// Build releaseinfo
|
||||
return new ReleaseInfo
|
||||
{
|
||||
Title = title,
|
||||
Description = title,
|
||||
Category = category, // Who seasons movies right
|
||||
Link = new Uri(DownloadUrl + torrentId),
|
||||
PublishDate = publishDate,
|
||||
Seeders = seeders,
|
||||
Peers = seeders,
|
||||
Files = files,
|
||||
Size = size,
|
||||
Grabs = grabs,
|
||||
Guid = guid,
|
||||
Comments = guid,
|
||||
DownloadVolumeFactor = 0, // ratioless tracker
|
||||
UploadVolumeFactor = 1
|
||||
};
|
||||
}
|
||||
|
||||
// Changes "Season 1" to "S01"
|
||||
private static string SeasonToShortSeason(string season)
|
||||
{
|
||||
var seasonMatch = new Regex(@"Season (?<seasonNumber>\d{1,2})").Match(season);
|
||||
if (seasonMatch.Success)
|
||||
{
|
||||
season = $"S{int.Parse(seasonMatch.Groups["seasonNumber"].Value):00}";
|
||||
}
|
||||
|
||||
return season;
|
||||
}
|
||||
|
||||
// Changes "xx.xx GB/MB" to bytes
|
||||
private static long ParseSizeToBytes(string strSize)
|
||||
{
|
||||
var sizeParts = strSize.Split(' ');
|
||||
if (sizeParts.Length != 2)
|
||||
throw new Exception($"We expected 2 size parts, instead we have {sizeParts.Length}.");
|
||||
|
||||
var size = double.Parse(sizeParts[0]);
|
||||
|
||||
switch (sizeParts[1].Trim())
|
||||
{
|
||||
case "GB":
|
||||
size = size*1000*1000*1000;
|
||||
break;
|
||||
case "MB":
|
||||
size = size*1000*1000;
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Unknown size type {sizeParts[1].Trim()}.");
|
||||
}
|
||||
|
||||
return (long) Math.Ceiling(size);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
242
src/Jackett/Indexers/MyAnonamouse.cs
Normal file
@@ -0,0 +1,242 @@
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class Myanonamouse : BaseIndexer, IIndexer
|
||||
{
|
||||
private string LoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "tor/js/loadSearch2.php"; } }
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public Myanonamouse(IIndexerManagerService i, IWebClient c, Logger l, IProtectionService ps)
|
||||
: base(name: "Myanonamouse",
|
||||
description: "Friendliness, Warmth and Sharing",
|
||||
link: "https://www.myanonamouse.net/",
|
||||
caps: new TorznabCapabilities(TorznabCatType.Books,
|
||||
TorznabCatType.AudioAudiobook,
|
||||
TorznabCatType.BooksComics,
|
||||
TorznabCatType.BooksEbook,
|
||||
TorznabCatType.BooksMagazines,
|
||||
TorznabCatType.BooksTechnical),
|
||||
manager: i,
|
||||
client: c,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
AddCategoryMapping("61", TorznabCatType.BooksComics);
|
||||
AddCategoryMapping("91", TorznabCatType.BooksTechnical);
|
||||
AddCategoryMapping("80", TorznabCatType.BooksTechnical);
|
||||
AddCategoryMapping("79", TorznabCatType.BooksMagazines);
|
||||
AddCategoryMapping("39", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("49", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("50", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("83", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("51", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("97", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("40", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("41", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("106", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("42", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("52", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("98", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("54", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("55", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("43", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("99", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("84", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("44", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("56", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("137", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("45", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("57", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("85", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("87", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("119", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("88", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("58", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("59", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("46", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("47", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("53", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("89", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("100", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("108", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("48", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("111", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("60", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("71", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("72", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("90", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("73", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("101", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("62", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("63", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("107", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("64", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("74", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("102", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("76", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("77", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("65", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("103", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("115", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("66", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("78", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("138", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("67", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("92", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("118", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("94", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("120", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("95", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("81", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("82", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("68", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("69", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("75", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("96", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("104", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("109", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("70", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("112", TorznabCatType.BooksEbook);
|
||||
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "email", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "returnto", "/" }
|
||||
};
|
||||
|
||||
var preRequest = await RequestStringWithCookiesAndRetry(LoginUrl, string.Empty);
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, preRequest.Cookies, true, SearchUrl, SiteLink);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("Search Results"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var errorMessage = dom["table.main table td.text"].Text().Trim().Replace("\n\t", " ");
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
NameValueCollection qParams = new NameValueCollection();
|
||||
qParams.Add("tor[text]", query.GetQueryString());
|
||||
qParams.Add("tor[srchIn][title]", "true");
|
||||
qParams.Add("tor[srchIn][author]", "true");
|
||||
qParams.Add("tor[searchType]", "all");
|
||||
qParams.Add("tor[searchIn]", "torrents");
|
||||
qParams.Add("tor[hash]", "");
|
||||
qParams.Add("tor[sortType]", "default");
|
||||
qParams.Add("tor[startNumber]", "0");
|
||||
|
||||
List<string> catList = MapTorznabCapsToTrackers(query);
|
||||
if (catList.Any())
|
||||
{
|
||||
foreach (string cat in catList)
|
||||
{
|
||||
qParams.Add("tor[cat][]", cat);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qParams.Add("tor[cat][]", "0");
|
||||
}
|
||||
|
||||
string urlSearch = SearchUrl;
|
||||
if (qParams.Count > 0)
|
||||
{
|
||||
urlSearch += $"?{qParams.GetQueryString()}";
|
||||
}
|
||||
|
||||
var response = await RequestStringWithCookiesAndRetry(urlSearch);
|
||||
|
||||
try
|
||||
{
|
||||
CQ dom = response.Content;
|
||||
var rows = dom["table[class='newTorTable'] > tbody > tr"];
|
||||
|
||||
foreach (IDomObject row in rows)
|
||||
{
|
||||
CQ torrentData = row.OuterHTML;
|
||||
CQ cells = row.Cq().Find("td");
|
||||
|
||||
if (cells.Any() && torrentData.Find("a[class='directDownload']").Any())
|
||||
{
|
||||
string title = torrentData.Find("a[class='title']").First().Text().Trim();
|
||||
string author = torrentData.Find("a[class='author']").First().Text().Trim();
|
||||
Uri link = new Uri(SiteLink + torrentData.Find("a[class='directDownload']").First().Attr("href").Trim().TrimStart('/'));
|
||||
Uri guid = new Uri(SiteLink + torrentData.Find("a[class='directDownload']").First().Attr("href").Trim().TrimStart('/'));
|
||||
long size = ReleaseInfo.GetBytes(cells.Elements.ElementAt(4).Cq().Text().Split('[')[1].TrimEnd(']'));
|
||||
int seeders = ParseUtil.CoerceInt(cells.Elements.ElementAt(6).Cq().Find("p").ElementAt(0).Cq().Text());
|
||||
int leechers = ParseUtil.CoerceInt(cells.Elements.ElementAt(6).Cq().Find("p").ElementAt(1).Cq().Text());
|
||||
|
||||
string pubDateStr = cells.Elements.ElementAt(5).Cq().Text().Split('[')[0];
|
||||
DateTime publishDate = DateTime.Parse(pubDateStr).ToLocalTime();
|
||||
|
||||
long category = 0;
|
||||
string cat = torrentData.Find("a[class='newCatLink']").First().Attr("href").Remove(0, "/tor/browse.php?tor[cat][]]=".Length);
|
||||
long.TryParse(cat, out category);
|
||||
|
||||
|
||||
var release = new ReleaseInfo();
|
||||
|
||||
release.Title = String.IsNullOrEmpty(author) ? title : String.Format("{0} by {1}", title, author);
|
||||
release.Guid = guid;
|
||||
release.Link = link;
|
||||
release.PublishDate = publishDate;
|
||||
release.Size = size;
|
||||
release.Description = release.Title;
|
||||
release.Seeders = seeders;
|
||||
release.Peers = seeders + leechers;
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800;
|
||||
release.Category = MapTrackerCatToNewznab(category.ToString());
|
||||
release.Comments = guid;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(response.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
223
src/Jackett/Indexers/NewRealWorld.cs
Normal file
@@ -0,0 +1,223 @@
|
||||
using Jackett.Utils.Clients;
|
||||
using NLog;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using CsQuery;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
using System.Text;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class NewRealWorld : BaseIndexer, IIndexer
|
||||
{
|
||||
string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
string BrowseUrl { get { return SiteLink + "browse.php"; } }
|
||||
|
||||
new ConfigurationDataBasicLoginWithRSSAndDisplay configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLoginWithRSSAndDisplay)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public NewRealWorld(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "New Real World",
|
||||
description: "A German general tracker.",
|
||||
link: "http://nrw-tracker.eu/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLoginWithRSSAndDisplay())
|
||||
{
|
||||
AddCategoryMapping(39, TorznabCatType.TVAnime); // Anime: HD|1080p
|
||||
AddCategoryMapping(38, TorznabCatType.TVAnime); // Anime: HD|720p
|
||||
AddCategoryMapping(1, TorznabCatType.TVAnime); // Anime: SD
|
||||
AddCategoryMapping(7, TorznabCatType.PCPhoneOther); // Appz: Handy-PDA
|
||||
AddCategoryMapping(36, TorznabCatType.PCMac); // Appz: Mac
|
||||
AddCategoryMapping(18, TorznabCatType.PC); // Appz: Sonstiges
|
||||
AddCategoryMapping(17, TorznabCatType.PC); // Appz: Win
|
||||
AddCategoryMapping(15, TorznabCatType.Audio); // Audio: DVD-R
|
||||
AddCategoryMapping(49, TorznabCatType.AudioLossless); // Audio: Flac
|
||||
AddCategoryMapping(30, TorznabCatType.AudioAudiobook); // Audio: Hörspiele
|
||||
AddCategoryMapping(14, TorznabCatType.AudioMP3); // Audio: MP3
|
||||
AddCategoryMapping(22, TorznabCatType.AudioVideo); // Audio: Videoclip
|
||||
AddCategoryMapping(19, TorznabCatType.Other); // Diverses: Sonstiges
|
||||
AddCategoryMapping(43, TorznabCatType.TVDocumentary); // Dokus: HD
|
||||
AddCategoryMapping(2, TorznabCatType.TVDocumentary); // Dokus: SD
|
||||
AddCategoryMapping(3, TorznabCatType.Books); // Ebooks: Bücher
|
||||
AddCategoryMapping(52, TorznabCatType.BooksComics); // Ebooks: Comics
|
||||
AddCategoryMapping(53, TorznabCatType.BooksMagazines); // Ebooks: Magazine
|
||||
AddCategoryMapping(55, TorznabCatType.BooksOther); // Ebooks: XXX
|
||||
AddCategoryMapping(54, TorznabCatType.BooksOther); // Ebooks: Zeitungen
|
||||
AddCategoryMapping(47, TorznabCatType.PCPhoneOther); // Games: Andere
|
||||
AddCategoryMapping(32, TorznabCatType.PCMac); // Games: Mac
|
||||
AddCategoryMapping(41, TorznabCatType.ConsoleNDS); // Games: NDS/3DS
|
||||
AddCategoryMapping(4, TorznabCatType.PCGames); // Games: PC
|
||||
AddCategoryMapping(5, TorznabCatType.ConsolePS3); // Games: PS2
|
||||
AddCategoryMapping(9, TorznabCatType.ConsolePS3); // Games: PS3
|
||||
AddCategoryMapping(6, TorznabCatType.ConsolePSP); // Games: PSP
|
||||
AddCategoryMapping(28, TorznabCatType.ConsoleWii); // Games: Wii
|
||||
AddCategoryMapping(31, TorznabCatType.ConsoleXbox); // Games: XboX
|
||||
AddCategoryMapping(51, TorznabCatType.Movies3D); // Movies: 3D
|
||||
AddCategoryMapping(37, TorznabCatType.MoviesBluRay); // Movies: BluRay
|
||||
AddCategoryMapping(25, TorznabCatType.MoviesHD); // Movies: HD|1080p
|
||||
AddCategoryMapping(29, TorznabCatType.MoviesHD); // Movies: HD|720p
|
||||
AddCategoryMapping(11, TorznabCatType.MoviesDVD); // Movies: SD|DVD-R
|
||||
AddCategoryMapping(8, TorznabCatType.MoviesSD); // Movies: SD|x264
|
||||
AddCategoryMapping(13, TorznabCatType.MoviesSD); // Movies: SD|XviD
|
||||
AddCategoryMapping(40, TorznabCatType.MoviesForeign); // Movies: US Movies
|
||||
AddCategoryMapping(33, TorznabCatType.TV); // Serien: DVD-R
|
||||
AddCategoryMapping(34, TorznabCatType.TVHD); // Serien: HD
|
||||
AddCategoryMapping(56, TorznabCatType.TVHD); // Serien: Packs|HD
|
||||
AddCategoryMapping(44, TorznabCatType.TVSD); // Serien: Packs|SD
|
||||
AddCategoryMapping(16, TorznabCatType.TVSD); // Serien: SD
|
||||
AddCategoryMapping(10, TorznabCatType.TVOTHER); // Serien: TV/Shows
|
||||
AddCategoryMapping(21, TorznabCatType.TVFOREIGN); // Serien: US TV
|
||||
AddCategoryMapping(24, TorznabCatType.TVSport); // Sport: Diverses
|
||||
AddCategoryMapping(23, TorznabCatType.TVSport); // Sport: Wrestling
|
||||
AddCategoryMapping(57, TorznabCatType.Movies); // Tracker - Crew: pmHD
|
||||
AddCategoryMapping(58, TorznabCatType.MoviesHD); // Ultra-HD: 4K
|
||||
AddCategoryMapping(46, TorznabCatType.XXXOther); // XXX: Diverses
|
||||
AddCategoryMapping(50, TorznabCatType.XXX); // XXX: HD
|
||||
AddCategoryMapping(45, TorznabCatType.XXXPacks); // XXX: Packs
|
||||
AddCategoryMapping(27, TorznabCatType.XXX); // XXX: SD
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
var pairs = new Dictionary<string, string>
|
||||
{
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "submit", "Log+in!" }
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, LoginUrl, true);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var errorMessage = dom["table.tableinborder"].Html();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
TimeZoneInfo.TransitionTime startTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 3, 0, 0), 3, 5, DayOfWeek.Sunday);
|
||||
TimeZoneInfo.TransitionTime endTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 4, 0, 0), 10, 5, DayOfWeek.Sunday);
|
||||
TimeSpan delta = new TimeSpan(1, 0, 0);
|
||||
TimeZoneInfo.AdjustmentRule adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(1999, 10, 1), DateTime.MaxValue.Date, delta, startTransition, endTransition);
|
||||
TimeZoneInfo.AdjustmentRule[] adjustments = { adjustment };
|
||||
TimeZoneInfo germanyTz = TimeZoneInfo.CreateCustomTimeZone("W. Europe Standard Time", new TimeSpan(1, 0, 0), "(GMT+01:00) W. Europe Standard Time", "W. Europe Standard Time", "W. Europe DST Time", adjustments);
|
||||
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = BrowseUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
queryCollection.Add("showsearch", "1");
|
||||
queryCollection.Add("incldead", "1");
|
||||
queryCollection.Add("orderby", "added");
|
||||
queryCollection.Add("sort", "desc");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
queryCollection.Add("search", searchString);
|
||||
}
|
||||
|
||||
var cats = MapTorznabCapsToTrackers(query);
|
||||
string cat = "0";
|
||||
if (cats.Count == 1)
|
||||
{
|
||||
cat = cats[0];
|
||||
}
|
||||
queryCollection.Add("cat", cat);
|
||||
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
var response = await RequestBytesWithCookies(searchUrl);
|
||||
var results = Encoding.GetEncoding("iso-8859-1").GetString(response.Content);
|
||||
try
|
||||
{
|
||||
CQ dom = results;
|
||||
var rows = dom["table.testtable> tbody > tr:has(td.tableb)"];
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.MinimumRatio = 0.75;
|
||||
release.MinimumSeedTime = 0;
|
||||
var qRow = row.Cq();
|
||||
|
||||
var qDetailsLink = qRow.Find("a[href^=details.php?id=]").First();
|
||||
release.Title = qDetailsLink.Text();
|
||||
|
||||
if (!query.MatchQueryStringAND(release.Title))
|
||||
continue;
|
||||
|
||||
var qCatLink = qRow.Find("a[href^=browse.php?cat=]").First();
|
||||
var qSeeders = qRow.Find("td > table.testtable > tbody > tr > td > strong:eq(3)");
|
||||
var qLeechers = qRow.Find("td > table.testtable > tbody > tr > td > strong:eq(4)");
|
||||
var qDateStr = qRow.Find("td > table.testtable > tbody > tr > td:eq(6)");
|
||||
var qSize = qRow.Find("td > table.testtable > tbody > tr > td > strong:eq(1)");
|
||||
var qDownloadLink = qRow.Find("a[href*=download]").First();
|
||||
|
||||
var catStr = qCatLink.Attr("href").Split('=')[1];
|
||||
release.Category = MapTrackerCatToNewznab(catStr);
|
||||
|
||||
var dlLink = qDownloadLink.Attr("href");
|
||||
if(dlLink.Contains("javascript")) // depending on the user agent the DL link is a javascript call
|
||||
{
|
||||
var dlLinkParts = dlLink.Split(new char[] { '\'', ',' });
|
||||
dlLink = SiteLink + "download/" + dlLinkParts[3] + "/" + dlLinkParts[5];
|
||||
}
|
||||
release.Link = new Uri(dlLink);
|
||||
release.Comments = new Uri(SiteLink + qDetailsLink.Attr("href"));
|
||||
release.Guid = release.Link;
|
||||
|
||||
var sizeStr = qSize.Text();
|
||||
logger.Error(sizeStr);
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr.Replace(".", "").Replace(",", "."));
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(qSeeders.Text());
|
||||
release.Peers = ParseUtil.CoerceInt(qLeechers.Text()) + release.Seeders;
|
||||
|
||||
var dateStr = qDateStr.Text().Replace('\xA0', ' ');
|
||||
var dateGerman = DateTime.SpecifyKind(DateTime.ParseExact(dateStr, "dd.MM.yyyy HH:mm:ss", CultureInfo.InvariantCulture), DateTimeKind.Unspecified);
|
||||
DateTime pubDateUtc = TimeZoneInfo.ConvertTimeToUtc(dateGerman, germanyTz);
|
||||
release.PublishDate = pubDateUtc;
|
||||
|
||||
var files = qRow.Find("td:contains(Datei) > strong ~ strong").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
if (qRow.Find("img[title=\"OnlyUpload\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
167
src/Jackett/Indexers/PassThePopcorn.cs
Normal file
@@ -0,0 +1,167 @@
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class PassThePopcorn : BaseIndexer, IIndexer
|
||||
{
|
||||
private string LoginUrl { get { return "https://tls.passthepopcorn.me/ajax.php?action=login"; } }
|
||||
private string indexUrl { get { return "https://tls.passthepopcorn.me/ajax.php?action=login"; } }
|
||||
private string SearchUrl { get { return "https://tls.passthepopcorn.me/torrents.php"; } }
|
||||
private string DetailURL { get { return "https://tls.passthepopcorn.me/torrents.php?torrentid="; } }
|
||||
private string AuthKey { get; set; }
|
||||
new ConfigurationDataBasicLoginWithFilterAndPasskey configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLoginWithFilterAndPasskey)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public PassThePopcorn(IIndexerManagerService i, Logger l, IWebClient c, IProtectionService ps)
|
||||
: base(name: "PassThePopcorn",
|
||||
description: "PassThePopcorn",
|
||||
link: "https://passthepopcorn.me/",
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: c,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLoginWithFilterAndPasskey(@"Enter filter options below to restrict search results.
|
||||
Separate options with a space if using more than one option.<br>Filter options available:
|
||||
<br><code>GoldenPopcorn</code><br><code>Scene</code><br><code>Checked</code>"))
|
||||
{
|
||||
AddCategoryMapping(1, TorznabCatType.Movies);
|
||||
AddCategoryMapping(1, TorznabCatType.MoviesForeign);
|
||||
AddCategoryMapping(1, TorznabCatType.MoviesOther);
|
||||
AddCategoryMapping(1, TorznabCatType.MoviesSD);
|
||||
AddCategoryMapping(1, TorznabCatType.MoviesHD);
|
||||
AddCategoryMapping(1, TorznabCatType.Movies3D);
|
||||
AddCategoryMapping(1, TorznabCatType.MoviesBluRay);
|
||||
AddCategoryMapping(1, TorznabCatType.MoviesDVD);
|
||||
AddCategoryMapping(1, TorznabCatType.MoviesWEBDL);
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
await DoLogin();
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
private async Task DoLogin()
|
||||
{
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "passkey", configData.Passkey.Value },
|
||||
{ "keeplogged", "1" },
|
||||
{ "login", "Log In!" }
|
||||
};
|
||||
|
||||
var response = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, indexUrl, SiteLink);
|
||||
JObject js_response = JObject.Parse(response.Content);
|
||||
await ConfigureIfOK(response.Cookies, response.Content != null && (string)js_response["Result"] != "Error", () =>
|
||||
{
|
||||
// Landing page wil have "Result":"Error" if log in fails
|
||||
string errorMessage = (string)js_response["Message"];
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
await DoLogin();
|
||||
|
||||
var releases = new List<ReleaseInfo>();
|
||||
bool configGoldenPopcornOnly = configData.FilterString.Value.ToLowerInvariant().Contains("goldenpopcorn");
|
||||
bool configSceneOnly = configData.FilterString.Value.ToLowerInvariant().Contains("scene");
|
||||
bool configCheckedOnly = configData.FilterString.Value.ToLowerInvariant().Contains("checked");
|
||||
string movieListSearchUrl;
|
||||
|
||||
if (string.IsNullOrEmpty(query.GetQueryString()))
|
||||
movieListSearchUrl = string.Format("{0}?json=noredirect", SearchUrl);
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(query.ImdbID))
|
||||
{
|
||||
movieListSearchUrl = string.Format("{0}?json=noredirect&searchstr={1}", SearchUrl, HttpUtility.UrlEncode(query.ImdbID));
|
||||
}
|
||||
else
|
||||
{
|
||||
movieListSearchUrl = string.Format("{0}?json=noredirect&searchstr={1}", SearchUrl, HttpUtility.UrlEncode(query.GetQueryString()));
|
||||
}
|
||||
}
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(movieListSearchUrl);
|
||||
try
|
||||
{
|
||||
//Iterate over the releases for each movie
|
||||
JObject js_results = JObject.Parse(results.Content);
|
||||
foreach (var movie in js_results["Movies"])
|
||||
{
|
||||
string movie_title = (string) movie["Title"];
|
||||
long movie_imdbid = long.Parse((string)movie["ImdbId"]);
|
||||
string movie_groupid = (string)movie["GroupId"];
|
||||
foreach (var torrent in movie["Torrents"])
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.Title = movie_title;
|
||||
release.Description = release.Title;
|
||||
release.Imdb = movie_imdbid;
|
||||
release.Comments = new Uri(string.Format("{0}?id={1}", SearchUrl, HttpUtility.UrlEncode(movie_groupid)));
|
||||
release.Guid = release.Comments;
|
||||
release.Size = long.Parse((string)torrent["Size"]);
|
||||
release.Seeders = int.Parse((string)torrent["Seeders"]);
|
||||
release.Peers = int.Parse((string)torrent["Leechers"]);
|
||||
release.PublishDate = DateTime.ParseExact((string)torrent["UploadTime"], "yyyy-MM-dd HH:mm:ss",
|
||||
CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime();
|
||||
release.Link = new Uri(string.Format("{0}?action=download&id={1}&authkey={2}&torrent_pass={3}",
|
||||
SearchUrl, HttpUtility.UrlEncode((string)torrent["Id"]), HttpUtility.UrlEncode(AuthKey), HttpUtility.UrlEncode(configData.Passkey.Value)));
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 345600;
|
||||
release.Category = 2000;
|
||||
|
||||
bool golden, scene, check;
|
||||
bool.TryParse((string)torrent["GoldenPopcorn"], out golden);
|
||||
bool.TryParse((string)torrent["Scene"], out scene);
|
||||
bool.TryParse((string)torrent["Checked"], out check);
|
||||
|
||||
|
||||
if (configGoldenPopcornOnly && !golden)
|
||||
{
|
||||
continue; //Skip release if user only wants GoldenPopcorn
|
||||
}
|
||||
if (configSceneOnly && !scene)
|
||||
{
|
||||
continue; //Skip release if user only wants Scene
|
||||
}
|
||||
if (configCheckedOnly && !check)
|
||||
{
|
||||
continue; //Skip release if user only wants Checked
|
||||
}
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
221
src/Jackett/Indexers/PirateTheNet.cs
Normal file
@@ -0,0 +1,221 @@
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class PirateTheNet : BaseIndexer, IIndexer
|
||||
{
|
||||
private string SearchUrl { get { return SiteLink + "torrentsutils.php"; } }
|
||||
private string LoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
private string CaptchaUrl { get { return SiteLink + "simpleCaptcha.php?numImages=1"; } }
|
||||
TimeZoneInfo germanyTz = TimeZoneInfo.CreateCustomTimeZone("W. Europe Standard Time", new TimeSpan(1, 0, 0), "W. Europe Standard Time", "W. Europe Standard Time");
|
||||
private readonly List<String> categories = new List<string>() { "1080P", "720P", "BDRip", "BluRay", "BRRip", "DVDR", "DVDRip", "FLAC", "MP3", "MP4", "Packs", "R5", "Remux", "TVRip", "WebRip" };
|
||||
|
||||
new ConfigurationDataBasicLoginWithRSSAndDisplay configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLoginWithRSSAndDisplay)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public PirateTheNet(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps)
|
||||
: base(name: "PirateTheNet",
|
||||
description: "A movie tracker",
|
||||
link: "http://piratethe.net/",
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLoginWithRSSAndDisplay())
|
||||
{
|
||||
this.configData.DisplayText.Value = "Only the results from the first search result page are shown, adjust your profile settings to show the maximum.";
|
||||
this.configData.DisplayText.Name = "Notice";
|
||||
|
||||
AddCategoryMapping("1080P", TorznabCatType.MoviesHD);
|
||||
AddCategoryMapping("720P", TorznabCatType.MoviesHD);
|
||||
AddCategoryMapping("BDRip", TorznabCatType.MoviesSD);
|
||||
AddCategoryMapping("BluRay", TorznabCatType.MoviesBluRay);
|
||||
AddCategoryMapping("BRRip", TorznabCatType.MoviesSD);
|
||||
AddCategoryMapping("DVDR", TorznabCatType.MoviesDVD);
|
||||
AddCategoryMapping("DVDRip", TorznabCatType.MoviesSD);
|
||||
AddCategoryMapping("FLAC", TorznabCatType.AudioLossless);
|
||||
AddCategoryMapping("MP3", TorznabCatType.AudioMP3);
|
||||
AddCategoryMapping("MP4", TorznabCatType.AudioOther);
|
||||
AddCategoryMapping("Packs", TorznabCatType.Movies);
|
||||
AddCategoryMapping("R5", TorznabCatType.MoviesDVD);
|
||||
AddCategoryMapping("Remux", TorznabCatType.Movies);
|
||||
AddCategoryMapping("TVRip", TorznabCatType.MoviesOther);
|
||||
AddCategoryMapping("WebRip", TorznabCatType.MoviesWEBDL);
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
var result1 = await RequestStringWithCookies(CaptchaUrl);
|
||||
var json1 = JObject.Parse(result1.Content);
|
||||
var captchaSelection = json1["images"][0]["hash"];
|
||||
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "captchaSelection", (string)captchaSelection }
|
||||
};
|
||||
|
||||
var result2 = await RequestLoginAndFollowRedirect(LoginUrl, pairs, result1.Cookies, true, null, null, true);
|
||||
|
||||
await ConfigureIfOK(result2.Cookies, result2.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
var errorMessage = "Login Failed";
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
List<ReleaseInfo> releases = new List<ReleaseInfo>();
|
||||
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = SearchUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
queryCollection.Add("action", "torrentstable");
|
||||
queryCollection.Add("viewtype", "0");
|
||||
queryCollection.Add("visiblecategories", "Action,Adventure,Animation,Biography,Comedy,Crime,Documentary,Drama,Eastern,Family,Fantasy,History,Holiday,Horror,Kids,Musical,Mystery,Romance,Sci-Fi,Short,Sports,Thriller,War,Western");
|
||||
queryCollection.Add("page", "1");
|
||||
queryCollection.Add("visibility", "showall");
|
||||
queryCollection.Add("compression", "showall");
|
||||
queryCollection.Add("sort", "added");
|
||||
queryCollection.Add("order", "DESC");
|
||||
queryCollection.Add("titleonly", "true");
|
||||
queryCollection.Add("packs", "showall");
|
||||
queryCollection.Add("bookmarks", "showall");
|
||||
queryCollection.Add("subscriptions", "showall");
|
||||
queryCollection.Add("skw", "showall");
|
||||
queryCollection.Add("advancedsearchparameters", "");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
// search keywords use OR by default and it seems like there's no way to change it, expect unwanted results
|
||||
queryCollection.Add("searchstring", searchString);
|
||||
}
|
||||
|
||||
var cats = MapTorznabCapsToTrackers(query);
|
||||
var hiddenqualities = "";
|
||||
if (cats.Count > 0)
|
||||
{
|
||||
hiddenqualities = String.Join(",", categories.Where(cat => !cats.Contains(cat)));
|
||||
}
|
||||
queryCollection.Add("hiddenqualities", hiddenqualities);
|
||||
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
try
|
||||
{
|
||||
CQ dom = results.Content;
|
||||
/*
|
||||
// parse logic for viewtype=1, unfortunately it's missing the release time so we can't use it
|
||||
var movieBlocks = dom["table.main"];
|
||||
foreach (var movieBlock in movieBlocks)
|
||||
{
|
||||
var qMovieBlock = movieBlock.Cq();
|
||||
|
||||
var movieLink = qMovieBlock.Find("tr > td[class=colhead] > a").First();
|
||||
var movieName = movieLink.Text();
|
||||
|
||||
var qDetailsBlock = qMovieBlock.Find("tr > td.torrentstd > table > tbody > tr");
|
||||
var qDetailsHeader = qDetailsBlock.ElementAt(0);
|
||||
var qDetailsTags = qDetailsBlock.ElementAt(1);
|
||||
var qTorrents = qDetailsBlock.Find("td.moviestorrentstd > table > tbody > tr:eq(0)");
|
||||
|
||||
foreach (var torrent in qTorrents)
|
||||
{
|
||||
var qTorrent = torrent.Cq();
|
||||
var qCatIcon = qTorrent.Find("td:eq(0) > img");
|
||||
var qDetailsLink = qTorrent.Find("td:eq(1) > a:eq(0)");
|
||||
var qSeeders = qTorrent.Find("td:eq(1) > b > a[alt=\"Number of Seeders\"]");
|
||||
var qLeechers = qTorrent.Find("td:eq(1) > span[alt=\"Number of Leechers\"]");
|
||||
var qDownloadLink = qTorrent.Find("td:eq(1) > a:has(img[alt=\"Download Torrent\"])");
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
var rows = dom["table.main > tbody > tr"];
|
||||
foreach (var row in rows.Skip(1))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 72 * 60 * 60;
|
||||
|
||||
var qRow = row.Cq();
|
||||
|
||||
var qDetailsLink = qRow.Find("td:eq(1) > a:eq(0)"); // link to the movie, not the actual torrent
|
||||
release.Title = qDetailsLink.Attr("alt");
|
||||
|
||||
var qCatIcon = qRow.Find("td:eq(0) > img");
|
||||
var qSeeders = qRow.Find("td:eq(8)");
|
||||
var qLeechers = qRow.Find("td:eq(9)");
|
||||
var qDownloadLink = qRow.Find("td > a:has(img[alt=\"Download Torrent\"])");
|
||||
var qPudDate = qRow.Find("td:eq(5) > nobr");
|
||||
var qSize = qRow.Find("td:eq(6)");
|
||||
|
||||
var catStr = qCatIcon.Attr("alt");
|
||||
release.Category = MapTrackerCatToNewznab(catStr);
|
||||
release.Link = new Uri(SiteLink + qDownloadLink.Attr("href").Substring(1));
|
||||
release.Title = qDetailsLink.Attr("alt");
|
||||
release.Comments = new Uri(SiteLink + qDetailsLink.Attr("href"));
|
||||
release.Guid = release.Link;
|
||||
|
||||
var dateStr = qPudDate.Text().Trim();
|
||||
DateTime pubDateUtc;
|
||||
var Timeparts = dateStr.Split(new char[] { ' ' }, 2)[1];
|
||||
if (dateStr.StartsWith("Today "))
|
||||
pubDateUtc = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) + DateTime.ParseExact(dateStr.Split(new char[] { ' ' }, 2)[1], "hh:mm tt", System.Globalization.CultureInfo.InvariantCulture).TimeOfDay;
|
||||
else if (dateStr.StartsWith("Yesterday "))
|
||||
pubDateUtc = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) +
|
||||
DateTime.ParseExact(dateStr.Split(new char[] { ' ' }, 2)[1], "hh:mm tt", System.Globalization.CultureInfo.InvariantCulture).TimeOfDay - TimeSpan.FromDays(1);
|
||||
else
|
||||
pubDateUtc = DateTime.SpecifyKind(DateTime.ParseExact(dateStr, "MMM d yyyy hh:mm tt", CultureInfo.InvariantCulture), DateTimeKind.Unspecified);
|
||||
|
||||
release.PublishDate = pubDateUtc.ToLocalTime();
|
||||
|
||||
var sizeStr = qSize.Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(qSeeders.Text());
|
||||
release.Peers = ParseUtil.CoerceInt(qLeechers.Text()) + release.Seeders;
|
||||
|
||||
var files = qRow.Find("td:nth-child(4)").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(8)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
release.DownloadVolumeFactor = 0; // ratioless
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
@@ -250,7 +250,15 @@ namespace Jackett.Indexers
|
||||
if (tags.Split(',').Length < 7)
|
||||
{
|
||||
queryCollection.Add("tags", tags);
|
||||
queryCollection.Add("tf", "any");
|
||||
if(!string.IsNullOrWhiteSpace(tags)) {
|
||||
// if tags are specified match any
|
||||
queryCollection.Add("tf", "any");
|
||||
}
|
||||
else
|
||||
{
|
||||
// if no tags are specified match all, with any we get random results
|
||||
queryCollection.Add("tf", "all");
|
||||
}
|
||||
}
|
||||
|
||||
if (queryCollection.Count > 0)
|
||||
@@ -297,6 +305,15 @@ namespace Jackett.Indexers
|
||||
var cat = row.ChildElements.ElementAt(0).ChildElements.ElementAt(0).GetAttribute("href").Replace("browse.php?", string.Empty);
|
||||
release.Category = MapTrackerResultCatToNewznab(cat);
|
||||
|
||||
var files = qRow.Find("td:nth-child(4)").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(9)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
release.DownloadVolumeFactor = 0; // ratioless
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
@@ -1,35 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Jackett.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using Jackett.Utils;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using CsQuery;
|
||||
using System.Web;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils.Clients;
|
||||
using System.Text.RegularExpressions;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class PrivateHD : AvistazTracker, IIndexer
|
||||
{
|
||||
public PrivateHD(IIndexerManagerService indexerManager, IWebClient webClient, Logger logger, IProtectionService protectionService)
|
||||
: base(name: "PrivateHD",
|
||||
desc: "BitTorrent site for High Quality, High Definition (HD) movies and TV Shows",
|
||||
link: "https://privatehd.to/",
|
||||
indexerManager: indexerManager,
|
||||
logger: logger,
|
||||
protectionService: protectionService,
|
||||
webClient: webClient
|
||||
)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Jackett.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using Jackett.Utils;
|
||||
using CsQuery;
|
||||
using System.Web;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils.Clients;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class PrivateHD : AvistazTracker, IIndexer
|
||||
{
|
||||
public PrivateHD(IIndexerManagerService indexerManager, IWebClient webClient, Logger logger, IProtectionService protectionService)
|
||||
: base(name: "PrivateHD",
|
||||
desc: "BitTorrent site for High Quality, High Definition (HD) movies and TV Shows",
|
||||
link: "https://privatehd.to/",
|
||||
indexerManager: indexerManager,
|
||||
logger: logger,
|
||||
protectionService: protectionService,
|
||||
webClient: webClient
|
||||
)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -151,6 +151,15 @@ namespace Jackett.Indexers
|
||||
|
||||
release.Category = MapTrackerCatToNewznab(cat);
|
||||
|
||||
var files = qRow.Find("td.ttr_size > a").Text().Split(' ')[0];
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = qRow.Find("td.ttr_snatched").Get(0).FirstChild.ToString();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
release.DownloadVolumeFactor = 1;
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
125
src/Jackett/Indexers/SceneFZ.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using Jackett.Utils.Clients;
|
||||
using NLog;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using CsQuery;
|
||||
using System.Web;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class SceneFZ : BaseIndexer, IIndexer
|
||||
{
|
||||
string LoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
|
||||
string BrowseUrl { get { return SiteLink + "ajax_browse.php"; } }
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public SceneFZ(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "SceneFZ",
|
||||
description: "Torrent tracker. Tracking over 50.000 torrent files.",
|
||||
link: "http://scenefz.net/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
AddCategoryMapping(32, TorznabCatType.Movies);
|
||||
AddCategoryMapping(33, TorznabCatType.TV);
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var pairs = new Dictionary<string, string>
|
||||
{
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value }
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, LoginUrl);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("Please wait..."), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var errorMessage = dom[".tableinborder:eq(1) td"].Text().Trim();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchUrl = BrowseUrl;
|
||||
var searchString = query.GetQueryString();
|
||||
|
||||
var cats = MapTorznabCapsToTrackers(query);
|
||||
string cat = "0";
|
||||
|
||||
if (cats.Count == 1)
|
||||
{
|
||||
cat = cats[0];
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString) || cat != "0")
|
||||
searchUrl += string.Format("?search={0}¶m_val=0&complex_search=0&incldead=mc{1}&orderby=added&sort=desc", HttpUtility.UrlEncode(searchString), cat);
|
||||
|
||||
var response = await RequestStringWithCookiesAndRetry(searchUrl, null, BrowseUrl);
|
||||
var results = response.Content;
|
||||
try
|
||||
{
|
||||
CQ dom = results;
|
||||
var rows = dom["td#browse-middle-td"];
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
var qRow = row.Cq();
|
||||
var qTitleLink = qRow.Find("table tbody tr:eq(0) td a").First();
|
||||
release.Title = qRow.Find("table tbody tr:eq(0) td a b").Text().Trim();
|
||||
release.Description = release.Title;
|
||||
release.Guid = new Uri(SiteLink + qTitleLink.Attr("href"));
|
||||
release.Comments = release.Guid;
|
||||
|
||||
//24.04.2016 16:44:57
|
||||
var dateStr = qRow.Find("table tbody tr:eq(1) td:eq(4)").Html().Replace(" ", " ").Trim();
|
||||
release.PublishDate = DateTime.ParseExact(dateStr, "dd.MM.yyyy HH:mm:ss", CultureInfo.InvariantCulture).AddHours(-2);
|
||||
|
||||
var qLink = qRow.First().Next().Find("a");
|
||||
release.Link = new Uri(SiteLink + qLink.Attr("href"));
|
||||
|
||||
var sizeStr = qRow.Find("table tbody tr:eq(1) td b").Text().Trim();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr.Replace(",", "."));
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(qRow.Find("table tbody tr:eq(1) td:eq(1) b:eq(0) font").Text().Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(qRow.Find("table tbody tr:eq(1) td:eq(1) b:eq(1) font").Text().Trim()) + release.Seeders;
|
||||
|
||||
var catId = qRow.First().Prev().Find("a").Attr("onclick").Substring(21, 2);
|
||||
release.Category = MapTrackerCatToNewznab(catId);
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -16,13 +16,14 @@ namespace Jackett.Indexers
|
||||
{
|
||||
public class SceneTime : BaseIndexer, IIndexer
|
||||
{
|
||||
private string StartPageUrl { get { return SiteLink + "login.php"; } }
|
||||
private string LoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "browse_API.php"; } }
|
||||
private string DownloadUrl { get { return SiteLink + "download.php/{0}/download.torrent"; } }
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
new ConfigurationDataRecaptchaLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
get { return (ConfigurationDataRecaptchaLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
@@ -35,7 +36,7 @@ namespace Jackett.Indexers
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin("For best results, change the 'Torrents per page' setting to the maximum in your profile on the SceneTime webpage."))
|
||||
configData: new ConfigurationDataRecaptchaLogin("For best results, change the 'Torrents per page' setting to the maximum in your profile on the SceneTime webpage."))
|
||||
{
|
||||
AddCategoryMapping(1, TorznabCatType.MoviesSD);
|
||||
AddCategoryMapping(3, TorznabCatType.MoviesDVD);
|
||||
@@ -78,14 +79,60 @@ namespace Jackett.Indexers
|
||||
AddCategoryMapping(11, TorznabCatType.AudioVideo);
|
||||
}
|
||||
|
||||
public override async Task<ConfigurationData> GetConfigurationForSetup()
|
||||
{
|
||||
var loginPage = await RequestStringWithCookies(StartPageUrl, string.Empty);
|
||||
CQ cq = loginPage.Content;
|
||||
var result = this.configData;
|
||||
result.Captcha.Version = "2";
|
||||
CQ recaptcha = cq.Find(".g-recaptcha").Attr("data-sitekey");
|
||||
if (recaptcha.Length != 0)
|
||||
{
|
||||
result.CookieHeader.Value = loginPage.Cookies;
|
||||
result.Captcha.SiteKey = cq.Find(".g-recaptcha").Attr("data-sitekey");
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
var stdResult = new ConfigurationDataBasicLogin();
|
||||
stdResult.Username.Value = configData.Username.Value;
|
||||
stdResult.Password.Value = configData.Password.Value;
|
||||
stdResult.CookieHeader.Value = loginPage.Cookies;
|
||||
return stdResult;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value }
|
||||
{ "password", configData.Password.Value },
|
||||
{ "g-recaptcha-response", configData.Captcha.Value }
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(configData.Captcha.Cookie))
|
||||
{
|
||||
CookieHeader = configData.Captcha.Cookie;
|
||||
try
|
||||
{
|
||||
var results = await PerformQuery(new TorznabQuery());
|
||||
if (results.Count() == 0)
|
||||
{
|
||||
throw new Exception("Your cookie did not work");
|
||||
}
|
||||
|
||||
SaveConfig();
|
||||
IsConfigured = true;
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
IsConfigured = false;
|
||||
throw new Exception("Your cookie did not work: " + e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, LoginUrl);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
@@ -116,12 +163,12 @@ namespace Jackett.Indexers
|
||||
}
|
||||
|
||||
var results = await PostDataWithCookiesAndRetry(SearchUrl, qParams);
|
||||
List<ReleaseInfo> releases = ParseResponse(results.Content);
|
||||
List<ReleaseInfo> releases = ParseResponse(query, results.Content);
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
public List<ReleaseInfo> ParseResponse(string htmlResponse)
|
||||
public List<ReleaseInfo> ParseResponse(TorznabQuery query, string htmlResponse)
|
||||
{
|
||||
List<ReleaseInfo> releases = new List<ReleaseInfo>();
|
||||
|
||||
@@ -152,6 +199,9 @@ namespace Jackett.Indexers
|
||||
var qDescCol = descCol.Cq();
|
||||
var qLink = qDescCol.Find("a");
|
||||
release.Title = qLink.Text();
|
||||
if (!query.MatchQueryStringAND(release.Title))
|
||||
continue;
|
||||
|
||||
release.Description = release.Title;
|
||||
release.Comments = new Uri(SiteLink + "/" + qLink.Attr("href"));
|
||||
release.Guid = release.Comments;
|
||||
@@ -166,6 +216,13 @@ namespace Jackett.Indexers
|
||||
release.Seeders = ParseUtil.CoerceInt(row.ChildElements.ElementAt(seedersIndex).Cq().Text().Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(leechersIndex).Cq().Text().Trim()) + release.Seeders;
|
||||
|
||||
if (row.Cq().Find("font > b:contains(Freeleech)").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ namespace Jackett.Indexers
|
||||
{
|
||||
public class SpeedCD : BaseIndexer, IIndexer
|
||||
{
|
||||
private string LoginUrl { get { return SiteLink + "take.login.php"; } }
|
||||
private string LoginUrl { get { return SiteLink + "takeElogin.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "browse.php"; } }
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
@@ -35,7 +35,8 @@ namespace Jackett.Indexers
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
configData: new ConfigurationDataBasicLogin(@"Speed.Cd have increased their security. If you are having problems please check the security tab in your Speed.Cd profile.
|
||||
eg. Geo Locking, your seedbox may be in a different country to the one where you login via your web browser"))
|
||||
{
|
||||
AddCategoryMapping("1", TorznabCatType.MoviesOther);
|
||||
AddCategoryMapping("42", TorznabCatType.Movies);
|
||||
@@ -70,24 +71,39 @@ namespace Jackett.Indexers
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
await DoLogin();
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
private async Task DoLogin()
|
||||
{
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, SiteLink);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("/browse.php"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var errorMessage = dom["h5"].First().Text().Trim();
|
||||
var errorMessage = dom.Text();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var loggedInCheck = await RequestStringWithCookies(SearchUrl);
|
||||
if (!loggedInCheck.Content.Contains("/logout.php"))
|
||||
{
|
||||
//Cookie appears to expire after a period of time or logging in to the site via browser
|
||||
await DoLogin();
|
||||
}
|
||||
|
||||
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
NameValueCollection qParams = new NameValueCollection();
|
||||
|
@@ -194,6 +194,12 @@ namespace Jackett.Indexers
|
||||
}
|
||||
else
|
||||
{
|
||||
// The TVChaos UK search requires an exact match of the search string.
|
||||
// But it seems like they just send the unfiltered search to the SQL server in a like query (LIKE '%$searchstring%').
|
||||
// So we replace any whitespace/special character with % to make the search more usable.
|
||||
Regex ReplaceRegex = new Regex("[^a-zA-Z0-9]+");
|
||||
searchString = ReplaceRegex.Replace(searchString, "%");
|
||||
|
||||
var searchParams = new Dictionary<string, string> {
|
||||
{ "do", "search" },
|
||||
{ "keywords", searchString },
|
||||
@@ -236,6 +242,19 @@ namespace Jackett.Indexers
|
||||
// If its not apps or audio we can only mark as general TV
|
||||
if (release.Category == 0)
|
||||
release.Category = 5030;
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(6)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (qRow.Find("img[alt*=\"Free Torrent\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
if (qRow.Find("img[alt*=\"x2 Torrent\"]").Length >= 1)
|
||||
release.UploadVolumeFactor = 2;
|
||||
else
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
|
@@ -104,7 +104,14 @@ namespace Jackett.Indexers
|
||||
movieListSearchUrl = SearchUrl;
|
||||
else
|
||||
{
|
||||
movieListSearchUrl = string.Format("{0}?action=basic&searchstr={1}", SearchUrl, HttpUtility.UrlEncode(query.GetQueryString()));
|
||||
if (!string.IsNullOrEmpty(query.ImdbID))
|
||||
{
|
||||
movieListSearchUrl = string.Format("{0}?action=basic&searchstr={1}", SearchUrl, HttpUtility.UrlEncode(query.ImdbID));
|
||||
}
|
||||
else
|
||||
{
|
||||
movieListSearchUrl = string.Format("{0}?action=basic&searchstr={1}", SearchUrl, HttpUtility.UrlEncode(query.GetQueryString()));
|
||||
}
|
||||
}
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(movieListSearchUrl);
|
||||
|
@@ -135,15 +135,15 @@ namespace Jackett.Indexers
|
||||
{
|
||||
CQ dom = results;
|
||||
|
||||
var rows = dom["#content table:eq(4) tr"];
|
||||
foreach (var row in rows.Skip(1))
|
||||
var rows = dom["table > tbody:has(tr > td.colhead) > tr:not(:has(td.colhead))"];
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
|
||||
var link = row.Cq().Find("td:eq(1) a:eq(1)").First();
|
||||
release.Guid = new Uri(SiteLink + link.Attr("href"));
|
||||
release.Comments = release.Guid;
|
||||
release.Title = link.Text().Trim();
|
||||
release.Title = link.Get(0).FirstChild.ToString();
|
||||
release.Description = release.Title;
|
||||
|
||||
// If we search an get no results, we still get a table just with no info.
|
||||
@@ -171,6 +171,20 @@ namespace Jackett.Indexers
|
||||
release.Seeders = ParseUtil.CoerceInt(row.Cq().Find("td:eq(8)").First().Text().Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(row.Cq().Find("td:eq(9)").First().Text().Trim()) + release.Seeders;
|
||||
|
||||
var files = row.Cq().Find("td:nth-child(3)").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = row.Cq().Find("td:nth-child(8)").Text();
|
||||
if (grabs != "----")
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (row.Cq().Find("font[color=\"green\"]:contains(F):contains(L)").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
@@ -21,27 +21,44 @@ namespace Jackett.Indexers
|
||||
{
|
||||
public class TorrentDay : BaseIndexer, IIndexer
|
||||
{
|
||||
private string StartPageUrl { get { return SiteLink + "login.php"; } }
|
||||
private string LoginUrl { get { return SiteLink + "tak3login.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "browse.php"; } }
|
||||
private string UseLink { get { return (!String.IsNullOrEmpty(this.configData.AlternateLink.Value) ? this.configData.AlternateLink.Value : SiteLink); } }
|
||||
private string StartPageUrl { get { return UseLink + "login.php"; } }
|
||||
private string LoginUrl { get { return UseLink + "tak3login.php"; } }
|
||||
private string SearchUrl { get { return UseLink + "browse.php"; } }
|
||||
private List<String> KnownURLs = new List<String> {
|
||||
"https://tdonline.org/",
|
||||
"https://secure.torrentday.com/",
|
||||
"https://torrentday.eu/",
|
||||
"https://torrentday.it/",
|
||||
"https://classic.torrentday.com/",
|
||||
"https://www.torrentday.com/",
|
||||
"https://td-update.com/",
|
||||
"https://www.torrentday.me/",
|
||||
"https://www.torrentday.ru/",
|
||||
"https://www.torrentday.com/",
|
||||
"https://www.td.af/",
|
||||
};
|
||||
|
||||
new ConfigurationDataRecaptchaLogin configData
|
||||
new ConfigurationDataRecaptchaLoginWithAlternateLink configData
|
||||
{
|
||||
get { return (ConfigurationDataRecaptchaLogin)base.configData; }
|
||||
get { return (ConfigurationDataRecaptchaLoginWithAlternateLink)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public TorrentDay(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "TorrentDay",
|
||||
description: "TorrentDay",
|
||||
link: "https://tdonline.org/",
|
||||
link: "https://torrentday.it/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataRecaptchaLogin())
|
||||
configData: new ConfigurationDataRecaptchaLoginWithAlternateLink())
|
||||
{
|
||||
this.configData.Instructions.Value = this.DisplayName + " has multiple URLs. The default (" + this.SiteLink + ") can be changed by entering a new value in the box below.";
|
||||
this.configData.Instructions.Value += "The following are some known URLs for " + this.DisplayName;
|
||||
this.configData.Instructions.Value += "<ul><li>" + String.Join("</li><li>", this.KnownURLs.ToArray()) + "</li></ul>";
|
||||
|
||||
AddCategoryMapping(29, TorznabCatType.TVAnime);
|
||||
AddCategoryMapping(28, TorznabCatType.PC);
|
||||
@@ -54,6 +71,7 @@ namespace Jackett.Indexers
|
||||
AddCategoryMapping(25, TorznabCatType.MoviesSD);
|
||||
AddCategoryMapping(11, TorznabCatType.MoviesHD);
|
||||
AddCategoryMapping(5, TorznabCatType.MoviesHD);
|
||||
AddCategoryMapping(48, TorznabCatType.Movies);
|
||||
AddCategoryMapping(3, TorznabCatType.MoviesSD);
|
||||
AddCategoryMapping(21, TorznabCatType.MoviesSD);
|
||||
AddCategoryMapping(22, TorznabCatType.MoviesForeign);
|
||||
@@ -61,9 +79,12 @@ namespace Jackett.Indexers
|
||||
AddCategoryMapping(44, TorznabCatType.MoviesSD);
|
||||
AddCategoryMapping(1, TorznabCatType.MoviesSD);
|
||||
|
||||
// Music foreign
|
||||
// Music packs
|
||||
// Music videos
|
||||
// Music
|
||||
AddCategoryMapping(17, TorznabCatType.AudioMP3);
|
||||
AddCategoryMapping(44, TorznabCatType.AudioLossless);
|
||||
AddCategoryMapping(23, TorznabCatType.AudioForeign);
|
||||
AddCategoryMapping(41, TorznabCatType.AudioOther);
|
||||
AddCategoryMapping(16, TorznabCatType.AudioVideo);
|
||||
|
||||
AddCategoryMapping(4, TorznabCatType.PCGames);
|
||||
// ps3
|
||||
@@ -79,6 +100,7 @@ namespace Jackett.Indexers
|
||||
AddCategoryMapping(26, TorznabCatType.TVSD);
|
||||
AddCategoryMapping(7, TorznabCatType.TVHD);
|
||||
AddCategoryMapping(2, TorznabCatType.TVSD);
|
||||
AddCategoryMapping(34, TorznabCatType.TV);
|
||||
|
||||
AddCategoryMapping(6, TorznabCatType.XXX);
|
||||
AddCategoryMapping(15, TorznabCatType.XXX);
|
||||
@@ -88,9 +110,10 @@ namespace Jackett.Indexers
|
||||
{
|
||||
var loginPage = await RequestStringWithCookies(StartPageUrl, string.Empty);
|
||||
CQ cq = loginPage.Content;
|
||||
var result = new ConfigurationDataRecaptchaLogin();
|
||||
var result = this.configData;
|
||||
result.CookieHeader.Value = loginPage.Cookies;
|
||||
result.Captcha.SiteKey = cq.Find(".g-recaptcha").Attr("data-sitekey");
|
||||
result.Captcha.Version = "2";
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -126,7 +149,7 @@ namespace Jackett.Indexers
|
||||
}
|
||||
}
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, configData.CookieHeader.Value, true, SiteLink, LoginUrl);
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, configData.CookieHeader.Value, true, UseLink, LoginUrl);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
@@ -151,8 +174,15 @@ namespace Jackett.Indexers
|
||||
var queryUrl = SearchUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
queryCollection.Add("search", searchString);
|
||||
if (query.QueryType == "TorrentPotato" && !string.IsNullOrWhiteSpace(query.ImdbID) && query.ImdbID.ToLower().StartsWith("tt"))
|
||||
{
|
||||
queryCollection.Add("search", query.ImdbID);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
queryCollection.Add("search", searchString);
|
||||
}
|
||||
|
||||
foreach (var cat in MapTorznabCapsToTrackers(query))
|
||||
queryCollection.Add("c" + cat, "1");
|
||||
@@ -179,9 +209,9 @@ namespace Jackett.Indexers
|
||||
release.MinimumSeedTime = 172800;
|
||||
release.Title = qRow.Find(".torrentName").Text();
|
||||
release.Description = release.Title;
|
||||
release.Guid = new Uri(SiteLink + qRow.Find(".torrentName").Attr("href"));
|
||||
release.Guid = new Uri(UseLink + qRow.Find(".torrentName").Attr("href"));
|
||||
release.Comments = release.Guid;
|
||||
release.Link = new Uri(SiteLink + qRow.Find(".dlLinksInfo > a").Attr("href"));
|
||||
release.Link = new Uri(UseLink + qRow.Find(".dlLinksInfo > a").Attr("href"));
|
||||
|
||||
var sizeStr = qRow.Find(".sizeInfo").Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
@@ -200,6 +230,13 @@ namespace Jackett.Indexers
|
||||
var cat = qRow.Find("td:eq(0) a").First().Attr("href").Substring(15);//browse.php?cat=24
|
||||
release.Category = MapTrackerCatToNewznab(cat);
|
||||
|
||||
if (qRow.Find("span.flTags").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
251
src/Jackett/Indexers/TorrentHeaven.cs
Normal file
@@ -0,0 +1,251 @@
|
||||
using Jackett.Utils.Clients;
|
||||
using NLog;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using CsQuery;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class TorrentHeaven : BaseIndexer, IIndexer
|
||||
{
|
||||
string IndexUrl { get { return SiteLink + "index.php"; } }
|
||||
string LoginCompleteUrl { get { return SiteLink + "index.php?strWebValue=account&strWebAction=login_complete&ancestry=verify"; } }
|
||||
|
||||
new ConfigurationDataCaptchaLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataCaptchaLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public TorrentHeaven(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "TorrentHeaven",
|
||||
description: "A German general tracker.",
|
||||
link: "https://torrentheaven.myfqdn.info/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataCaptchaLogin())
|
||||
{
|
||||
AddCategoryMapping(1, TorznabCatType.PCGames); // GAMES/PC
|
||||
AddCategoryMapping(3, TorznabCatType.Console); // GAMES/Sonstige
|
||||
AddCategoryMapping(59, TorznabCatType.ConsolePS4); // GAMES/PlayStation
|
||||
AddCategoryMapping(60, TorznabCatType.ConsolePSP); // GAMES/PSP
|
||||
AddCategoryMapping(63, TorznabCatType.ConsoleWii); // GAMES/Wii
|
||||
AddCategoryMapping(67, TorznabCatType.ConsoleXbox360); // GAMES/XBOX 360
|
||||
AddCategoryMapping(68, TorznabCatType.PCPhoneOther); // GAMES/PDA / Handy
|
||||
AddCategoryMapping(72, TorznabCatType.ConsoleNDS); // GAMES/NDS
|
||||
|
||||
AddCategoryMapping(7 , TorznabCatType.MoviesDVD); // MOVIES/DVD
|
||||
AddCategoryMapping(8, TorznabCatType.MoviesSD); // MOVIES/SD
|
||||
AddCategoryMapping(37, TorznabCatType.MoviesDVD); // MOVIES/DVD Spezial
|
||||
AddCategoryMapping(41, TorznabCatType.MoviesForeign); // MOVIES/International
|
||||
AddCategoryMapping(101, TorznabCatType.MoviesHD); // MOVIES/720p
|
||||
AddCategoryMapping(102, TorznabCatType.MoviesHD); // MOVIES/1080p
|
||||
AddCategoryMapping(103, TorznabCatType.MoviesHD); // MOVIES/AVCHD
|
||||
AddCategoryMapping(104, TorznabCatType.MoviesBluRay); // MOVIES/Bluray
|
||||
AddCategoryMapping(106, TorznabCatType.Movies3D); // MOVIES/3D
|
||||
|
||||
AddCategoryMapping(14, TorznabCatType.Audio); // AUDIO/Musik
|
||||
AddCategoryMapping(15, TorznabCatType.AudioAudiobook); // AUDIO/Hörbücher
|
||||
AddCategoryMapping(16, TorznabCatType.AudioAudiobook); // AUDIO/Hörspiele
|
||||
AddCategoryMapping(36, TorznabCatType.AudioLossless); // AUDIO/Flac
|
||||
AddCategoryMapping(42, TorznabCatType.AudioOther); // AUDIO/Soundtracks
|
||||
AddCategoryMapping(58, TorznabCatType.AudioVideo); // AUDIO/Musikvideos
|
||||
|
||||
AddCategoryMapping(18, TorznabCatType.TVSD); // TV/Serien SD
|
||||
AddCategoryMapping(19, TorznabCatType.TVHD); // TV/Serien HD 720p
|
||||
AddCategoryMapping(20, TorznabCatType.TVHD); // TV/Serien HD 1080p
|
||||
AddCategoryMapping(49, TorznabCatType.TVSD); // TV/Serien DVD
|
||||
AddCategoryMapping(51, TorznabCatType.TVDocumentary); // TV/Doku SD
|
||||
AddCategoryMapping(52, TorznabCatType.TVDocumentary); // TV/Doku HD
|
||||
AddCategoryMapping(53, TorznabCatType.TV); // TV/Serien Complete Packs
|
||||
AddCategoryMapping(54, TorznabCatType.TVSport); // TV/Sport
|
||||
AddCategoryMapping(66, TorznabCatType.TVFOREIGN); // TV/International
|
||||
|
||||
AddCategoryMapping(22, TorznabCatType.Books); // MISC/EBooks
|
||||
AddCategoryMapping(24, TorznabCatType.Other); // MISC/Sonstiges
|
||||
AddCategoryMapping(25, TorznabCatType.Other); // MISC/Tonspuren
|
||||
AddCategoryMapping(108, TorznabCatType.TVAnime); // MISC/Anime
|
||||
|
||||
AddCategoryMapping(28, TorznabCatType.PC); // APPLICATIONS/PC
|
||||
AddCategoryMapping(29, TorznabCatType.PCPhoneOther); // APPLICATIONS/Mobile
|
||||
AddCategoryMapping(30, TorznabCatType.PC); // APPLICATIONS/Sonstige
|
||||
AddCategoryMapping(70, TorznabCatType.PC); // APPLICATIONS/Linux
|
||||
AddCategoryMapping(71, TorznabCatType.PCMac); // APPLICATIONS/Mac
|
||||
}
|
||||
|
||||
public override async Task<ConfigurationData> GetConfigurationForSetup()
|
||||
{
|
||||
var loginPage = await RequestStringWithCookies(IndexUrl, string.Empty);
|
||||
CQ dom = loginPage.Content;
|
||||
CQ qCaptchaImg = dom.Find("td.tablea > img").First();
|
||||
if(qCaptchaImg.Length == 1) {
|
||||
var CaptchaUrl = SiteLink + qCaptchaImg.Attr("src");
|
||||
var captchaImage = await RequestBytesWithCookies(CaptchaUrl, loginPage.Cookies);
|
||||
configData.CaptchaImage.Value = captchaImage.Content;
|
||||
}
|
||||
configData.CaptchaCookie.Value = loginPage.Cookies;
|
||||
return configData;
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
var pairs = new Dictionary<string, string>
|
||||
{
|
||||
{ "strWebAction", "login" },
|
||||
{ "strWebValue", "account" },
|
||||
{ "jsenabled", "1" },
|
||||
{ "screenwidth", "2560" },
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value }
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(configData.CaptchaText.Value))
|
||||
{
|
||||
pairs.Add("proofcode", configData.CaptchaText.Value);
|
||||
}
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(IndexUrl, pairs, configData.CaptchaCookie.Value, true, null, IndexUrl, true);
|
||||
if (result.Content == null || !result.Content.Contains("login_complete"))
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var errorMessage = dom["table > tbody > tr > td[valign=top][width=100%]"].Html();
|
||||
if(errorMessage.Length == 0)
|
||||
errorMessage = result.Content;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
}
|
||||
|
||||
var result2 = await RequestStringWithCookies(LoginCompleteUrl, result.Cookies);
|
||||
await ConfigureIfOK(result2.Cookies, result2.Cookies != null && result2.Cookies.Contains("pass"), () =>
|
||||
{
|
||||
var errorMessage = "Didn't get a user/pass cookie";
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
TimeZoneInfo.TransitionTime startTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 3, 0, 0), 3, 5, DayOfWeek.Sunday);
|
||||
TimeZoneInfo.TransitionTime endTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 4, 0, 0), 10, 5, DayOfWeek.Sunday);
|
||||
TimeSpan delta = new TimeSpan(1, 0, 0);
|
||||
TimeZoneInfo.AdjustmentRule adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(1999, 10, 1), DateTime.MaxValue.Date, delta, startTransition, endTransition);
|
||||
TimeZoneInfo.AdjustmentRule[] adjustments = { adjustment };
|
||||
TimeZoneInfo germanyTz = TimeZoneInfo.CreateCustomTimeZone("W. Europe Standard Time", new TimeSpan(1, 0, 0), "(GMT+01:00) W. Europe Standard Time", "W. Europe Standard Time", "W. Europe DST Time", adjustments);
|
||||
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = IndexUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
queryCollection.Add("strWebValue", "torrent");
|
||||
queryCollection.Add("strWebAction", "search");
|
||||
queryCollection.Add("sort", "torrent_added");
|
||||
queryCollection.Add("by", "d");
|
||||
queryCollection.Add("type", "0");
|
||||
queryCollection.Add("do_search", "suchen");
|
||||
queryCollection.Add("time", "0");
|
||||
queryCollection.Add("details", "title");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
queryCollection.Add("searchstring", searchString);
|
||||
}
|
||||
|
||||
foreach (var cat in MapTorznabCapsToTrackers(query))
|
||||
{
|
||||
queryCollection.Add("dirs" + cat, "1");
|
||||
}
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
logger.Error(searchUrl);
|
||||
logger.Error(CookieHeader);
|
||||
var response = await RequestBytesWithCookies(searchUrl);
|
||||
var results = Encoding.GetEncoding("iso-8859-1").GetString(response.Content);
|
||||
try
|
||||
{
|
||||
CQ dom = results;
|
||||
var rows = dom["table.torrenttable > tbody > tr"];
|
||||
|
||||
foreach (var row in rows.Skip(1))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.MinimumRatio = 0.8;
|
||||
release.MinimumSeedTime = 0;
|
||||
var qRow = row.Cq();
|
||||
|
||||
var qDetailsLink = qRow.Find("a[href^=index.php?strWebValue=torrent&strWebAction=details]").First();
|
||||
release.Title = qDetailsLink.Text();
|
||||
|
||||
var qCatLink = qRow.Find("a[href^=index.php?strWebValue=torrent&strWebAction=search&dir=]").First();
|
||||
var qDLLink = qRow.Find("a[href^=index.php?strWebValue=torrent&strWebAction=download&id=]").First();
|
||||
var qSeeders = qRow.Find("td.column1:eq(3)");
|
||||
var qLeechers = qRow.Find("td.column2:eq(3)");
|
||||
var qDateStr = qRow.Find("font:has(a)").First();
|
||||
var qSize = qRow.Find("td.column2[align=center]").First();
|
||||
|
||||
var catStr = qCatLink.Attr("href").Split('=')[3].Split('#')[0];
|
||||
release.Category = MapTrackerCatToNewznab(catStr);
|
||||
|
||||
release.Link = new Uri(SiteLink + qDLLink.Attr("href"));
|
||||
release.Comments = new Uri(SiteLink + qDetailsLink.Attr("href"));
|
||||
release.Guid = release.Link;
|
||||
|
||||
var sizeStr = qSize.Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(qSeeders.Text());
|
||||
release.Peers = ParseUtil.CoerceInt(qLeechers.Text()) + release.Seeders;
|
||||
|
||||
var dateStr = qDateStr.Text().Trim();
|
||||
var dateStrParts = dateStr.Split();
|
||||
DateTime dateGerman;
|
||||
if (dateStrParts[0] == "Heute")
|
||||
dateGerman = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) + TimeSpan.Parse(dateStrParts[1]);
|
||||
else if (dateStrParts[0] == "Gestern")
|
||||
dateGerman = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) + TimeSpan.Parse(dateStrParts[1]) - TimeSpan.FromDays(1);
|
||||
else
|
||||
dateGerman = DateTime.SpecifyKind(DateTime.ParseExact(dateStrParts[0]+ dateStrParts[1], "dd.MM.yyyyHH:mm", CultureInfo.InvariantCulture), DateTimeKind.Unspecified);
|
||||
|
||||
DateTime pubDateUtc = TimeZoneInfo.ConvertTimeToUtc(dateGerman, germanyTz);
|
||||
release.PublishDate = pubDateUtc.ToLocalTime();
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(7)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (qRow.Find("img[src=\"themes/images/freeleech.png\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else if (qRow.Find("img[src=\"themes/images/DL50.png\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0.5;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -39,7 +39,7 @@ namespace Jackett.Indexers
|
||||
logger: l,
|
||||
p: ps,
|
||||
downloadBase: "https://www.torrentleech.org/download/",
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
configData: new ConfigurationDataBasicLogin("For best results, change the 'Default Number of Torrents per Page' setting to the maximum in your profile on the TorrentLeech webpage."))
|
||||
{
|
||||
|
||||
AddCategoryMapping(8, TorznabCatType.MoviesSD); // cam
|
||||
@@ -82,6 +82,12 @@ namespace Jackett.Indexers
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
await DoLogin();
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
private async Task DoLogin()
|
||||
{
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
@@ -97,13 +103,20 @@ namespace Jackett.Indexers
|
||||
var errorMessage = messageEl.Text().Trim();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var loggedInCheck = await RequestStringWithCookies(SearchUrl);
|
||||
if (!loggedInCheck.Content.Contains("/logout.php"))
|
||||
{
|
||||
//Cookie appears to expire after a period of time or logging in to the site via browser
|
||||
await DoLogin();
|
||||
}
|
||||
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchString = query.GetQueryString();
|
||||
var searchString = query.GetQueryString();
|
||||
searchString = searchString.Replace('-', ' '); // remove dashes as they exclude search strings
|
||||
var searchUrl = SearchUrl;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
@@ -151,6 +164,10 @@ namespace Jackett.Indexers
|
||||
release.Link = new Uri(SiteLink + qRow.Find(".quickdownload > a").Attr("href").Substring(1));
|
||||
|
||||
var dateString = qRow.Find(".name")[0].InnerText.Trim().Replace(" ", string.Empty).Replace("Addedinon", string.Empty);
|
||||
|
||||
//Fix for issue 2016-04-30
|
||||
dateString = dateString.Replace("\r", "").Replace("\n", "");
|
||||
|
||||
//"2015-04-25 23:38:12"
|
||||
//"yyyy-MMM-dd hh:mm:ss"
|
||||
release.PublishDate = DateTime.ParseExact(dateString, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture);
|
||||
@@ -164,6 +181,12 @@ namespace Jackett.Indexers
|
||||
var category = qRow.Find(".category a").Attr("href").Replace("/torrents/browse/index/categories/", string.Empty);
|
||||
release.Category = MapTrackerCatToNewznab(category);
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(5)").Get(0).FirstChild.ToString();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
release.DownloadVolumeFactor = 1;
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
205
src/Jackett/Indexers/TorrentNetwork.cs
Normal file
@@ -0,0 +1,205 @@
|
||||
using Jackett.Utils.Clients;
|
||||
using NLog;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using CsQuery;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
using System.Text;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class TorrentNetwork : BaseIndexer, IIndexer
|
||||
{
|
||||
string LoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
string BrowseUrl { get { return SiteLink + "browse.php"; } }
|
||||
|
||||
new ConfigurationDataBasicLoginWithRSSAndDisplay configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLoginWithRSSAndDisplay)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public TorrentNetwork(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "Torrent Network",
|
||||
description: "A German general tracker.",
|
||||
link: "https://tntracker.org/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLoginWithRSSAndDisplay())
|
||||
{
|
||||
AddCategoryMapping(1, TorznabCatType.AudioAudiobook); // aBook
|
||||
AddCategoryMapping(4, TorznabCatType.PCMac); // App|Mac
|
||||
AddCategoryMapping(5, TorznabCatType.PC); // App|Win
|
||||
AddCategoryMapping(7, TorznabCatType.TVDocumentary); // Docu|HD
|
||||
AddCategoryMapping(6, TorznabCatType.TVDocumentary); // Docu|SD
|
||||
AddCategoryMapping(8, TorznabCatType.Books); // eBook
|
||||
AddCategoryMapping(10, TorznabCatType.PCGames); // Game|PC
|
||||
AddCategoryMapping(13, TorznabCatType.ConsolePS4); // Game|PSX
|
||||
AddCategoryMapping(12, TorznabCatType.ConsoleWii); // Game|Wii
|
||||
AddCategoryMapping(14, TorznabCatType.ConsoleXbox); // Game|XBOX
|
||||
AddCategoryMapping(30, TorznabCatType.Other); // Misc
|
||||
AddCategoryMapping(17, TorznabCatType.MoviesHD); // Movie|DE|1080p
|
||||
AddCategoryMapping(20, TorznabCatType.MoviesHD); // Movie|DE|2160p
|
||||
AddCategoryMapping(36, TorznabCatType.Movies3D); // Movie|DE|3D
|
||||
AddCategoryMapping(18, TorznabCatType.MoviesHD); // Movie|DE|720p
|
||||
AddCategoryMapping(34, TorznabCatType.TVAnime); // Movie|DE|Anime
|
||||
AddCategoryMapping(19, TorznabCatType.MoviesBluRay); // Movie|DE|BluRay
|
||||
AddCategoryMapping(45, TorznabCatType.Movies); // Movie|DE|Remux
|
||||
AddCategoryMapping(24, TorznabCatType.MoviesSD); // Movie|DE|SD
|
||||
AddCategoryMapping(39, TorznabCatType.Movies); // Movie|EN/JP|Anime
|
||||
AddCategoryMapping(43, TorznabCatType.MoviesHD); // Movie|EN|1080p
|
||||
AddCategoryMapping(37, TorznabCatType.MoviesHD); // Movie|EN|2160p
|
||||
AddCategoryMapping(35, TorznabCatType.MoviesHD); // Movie|EN|720p
|
||||
AddCategoryMapping(38, TorznabCatType.MoviesBluRay); // Movie|EN|BluRay
|
||||
AddCategoryMapping(46, TorznabCatType.Movies); // Movie|EN|Remux
|
||||
AddCategoryMapping(22, TorznabCatType.MoviesSD); // Movie|EN|SD
|
||||
AddCategoryMapping(44, TorznabCatType.AudioLossless); // Music|Flac
|
||||
AddCategoryMapping(25, TorznabCatType.AudioMP3); // Music|MP3
|
||||
AddCategoryMapping(26, TorznabCatType.AudioVideo); // Music|Video
|
||||
AddCategoryMapping(31, TorznabCatType.TVSport); // Sport
|
||||
AddCategoryMapping(2, TorznabCatType.TVAnime); // TV|DE|Anime
|
||||
AddCategoryMapping(28, TorznabCatType.TVHD); // TV|DE|HD
|
||||
AddCategoryMapping(16, TorznabCatType.TV); // TV|DE|Pack
|
||||
AddCategoryMapping(27, TorznabCatType.TVSD); // TV|DE|SD
|
||||
AddCategoryMapping(41, TorznabCatType.TVAnime); // TV|EN/JP|Anime
|
||||
AddCategoryMapping(40, TorznabCatType.TVHD); // TV|EN|HD
|
||||
AddCategoryMapping(42, TorznabCatType.TV); // TV|EN|Pack
|
||||
AddCategoryMapping(29, TorznabCatType.TVSD); // TV|EN|SD
|
||||
AddCategoryMapping(33, TorznabCatType.XXX); // XXX|HD
|
||||
AddCategoryMapping(32, TorznabCatType.XXX); // XXX|SD
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
var pairs = new Dictionary<string, string>
|
||||
{
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value }
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, LoginUrl, true);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var errorMessage = dom["table.tableinborder"].Html();
|
||||
errorMessage = result.Content;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
TimeZoneInfo.TransitionTime startTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 3, 0, 0), 3, 5, DayOfWeek.Sunday);
|
||||
TimeZoneInfo.TransitionTime endTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 4, 0, 0), 10, 5, DayOfWeek.Sunday);
|
||||
TimeSpan delta = new TimeSpan(1, 0, 0);
|
||||
TimeZoneInfo.AdjustmentRule adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(1999, 10, 1), DateTime.MaxValue.Date, delta, startTransition, endTransition);
|
||||
TimeZoneInfo.AdjustmentRule[] adjustments = { adjustment };
|
||||
TimeZoneInfo germanyTz = TimeZoneInfo.CreateCustomTimeZone("W. Europe Standard Time", new TimeSpan(1, 0, 0), "(GMT+01:00) W. Europe Standard Time", "W. Europe Standard Time", "W. Europe DST Time", adjustments);
|
||||
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = BrowseUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
queryCollection.Add("incldead", "1");
|
||||
queryCollection.Add("_by", "0");
|
||||
queryCollection.Add("sort", "4");
|
||||
queryCollection.Add("type", "desc");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
queryCollection.Add("search", searchString);
|
||||
}
|
||||
|
||||
foreach (var cat in MapTorznabCapsToTrackers(query))
|
||||
{
|
||||
queryCollection.Add("c" + cat, "1");
|
||||
}
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
var response = await RequestBytesWithCookies(searchUrl);
|
||||
var results = Encoding.GetEncoding("iso-8859-1").GetString(response.Content);
|
||||
try
|
||||
{
|
||||
CQ dom = results;
|
||||
var rows = dom["table[border=1] > tbody > tr:has(td.torrenttable)"];
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.MinimumRatio = 0.8;
|
||||
release.MinimumSeedTime = 48 * 60 * 60;
|
||||
|
||||
var qRow = row.Cq();
|
||||
|
||||
var qDetailsLink = qRow.Find("a[href^=details.php?id=]").First();
|
||||
var qTitle = qDetailsLink.Find("b").First();
|
||||
release.Title = qTitle.Text();
|
||||
|
||||
var qCatLink = qRow.Find("a[href^=browse.php?cat=]").First();
|
||||
var qDLLink = qRow.Find("a.download").First();
|
||||
var qSeeders = qRow.Find("td.torrenttable:eq(7)");
|
||||
var qLeechers = qRow.Find("td.torrenttable:eq(8)");
|
||||
var qDateStr = qRow.Find("td.torrenttable:eq(4)").First();
|
||||
var qSize = qRow.Find("td.torrenttable:eq(5)").First();
|
||||
|
||||
var catStr = qCatLink.Attr("href").Split('=')[1].Split('\'')[0];
|
||||
release.Category = MapTrackerCatToNewznab(catStr);
|
||||
|
||||
release.Link = new Uri(SiteLink + qDLLink.Attr("href"));
|
||||
release.Comments = new Uri(SiteLink + qDetailsLink.Attr("href"));
|
||||
release.Guid = release.Link;
|
||||
|
||||
var sizeStr = qSize.Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(qSeeders.Text());
|
||||
release.Peers = ParseUtil.CoerceInt(qLeechers.Text()) + release.Seeders;
|
||||
|
||||
var dateStr = qDateStr.Text().Trim();
|
||||
DateTime dateGerman = DateTime.SpecifyKind(DateTime.ParseExact(dateStr, "MMM d yyyy HH:mm", CultureInfo.InvariantCulture), DateTimeKind.Unspecified);
|
||||
|
||||
DateTime pubDateUtc = TimeZoneInfo.ConvertTimeToUtc(dateGerman, germanyTz);
|
||||
release.PublishDate = pubDateUtc.ToLocalTime();
|
||||
|
||||
var files = qRow.Find("td:nth-child(4)").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(8)").Get(0).FirstChild.ToString();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (qRow.Find("img[src=\"pic/torrent_ou.gif\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else if (qRow.Find("font[color=\"gray\"]:contains(50% Down)").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0.5;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,13 +15,14 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class TorrentShack : BaseIndexer, IIndexer
|
||||
{
|
||||
private string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "torrents.php?searchstr={0}&release_type=both&searchtags=&tags_type=0&order_by=s3&order_way=desc&torrent_preset=all&filter_cat%5B600%5D=1&filter_cat%5B620%5D=1&filter_cat%5B700%5D=1&filter_cat%5B981%5D=1&filter_cat%5B980%5D=1"; } }
|
||||
private string SearchUrl { get { return SiteLink + "torrents.php"; } }
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
@@ -39,7 +40,36 @@ namespace Jackett.Indexers
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
{
|
||||
AddCategoryMapping(600, TorznabCatType.TVHD); // TV/HD
|
||||
AddCategoryMapping(960, TorznabCatType.MoviesForeign); // Foreign
|
||||
AddCategoryMapping(300, TorznabCatType.MoviesHD); // Movies/HD
|
||||
AddCategoryMapping(200, TorznabCatType.PCGames); // Games/PC
|
||||
AddCategoryMapping(100, TorznabCatType.PC0day); // Apps/PC
|
||||
AddCategoryMapping(450, TorznabCatType.AudioMP3); // Music/MP3
|
||||
AddCategoryMapping(280, TorznabCatType.PCPhoneOther); // HandHeld
|
||||
AddCategoryMapping(620, TorznabCatType.TVSD); // TV/SD
|
||||
AddCategoryMapping(320, TorznabCatType.MoviesOther); // REMUX
|
||||
AddCategoryMapping(400, TorznabCatType.MoviesSD); // Movies/SD
|
||||
AddCategoryMapping(240, TorznabCatType.ConsolePS3); // Games/PS3
|
||||
AddCategoryMapping(150, TorznabCatType.PC0day); // Apps/misc
|
||||
AddCategoryMapping(480, TorznabCatType.AudioLossless); // Music/FLAC
|
||||
AddCategoryMapping(180, TorznabCatType.BooksEbook); // eBooks
|
||||
AddCategoryMapping(700, TorznabCatType.TVOTHER); // TV/DVDrip
|
||||
AddCategoryMapping(970, TorznabCatType.MoviesBluRay); // Full Blu-ray
|
||||
AddCategoryMapping(350, TorznabCatType.MoviesDVD); // Movies/DVD-R
|
||||
AddCategoryMapping(260, TorznabCatType.ConsoleXbox360); // Games/Xbox360
|
||||
AddCategoryMapping(500, TorznabCatType.AudioVideo); // Music/Videos
|
||||
AddCategoryMapping(181, TorznabCatType.AudioAudiobook); // AudioBooks
|
||||
AddCategoryMapping(981, TorznabCatType.TVHD); // TV-HD Pack
|
||||
AddCategoryMapping(850, TorznabCatType.TVAnime); // Anime
|
||||
AddCategoryMapping(982, TorznabCatType.MoviesHD); // Movies-HD Pack
|
||||
AddCategoryMapping(986, TorznabCatType.PCGames); // Games Pack
|
||||
AddCategoryMapping(984, TorznabCatType.AudioMP3); // MP3 Pack
|
||||
AddCategoryMapping(800, TorznabCatType.Other); // Misc
|
||||
AddCategoryMapping(980, TorznabCatType.TVSD); // TV-SD Pack
|
||||
AddCategoryMapping(983, TorznabCatType.TVSD); // Movies-SD Pack
|
||||
AddCategoryMapping(985, TorznabCatType.AudioLossless); // FLAC Pack
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
@@ -69,8 +99,29 @@ namespace Jackett.Indexers
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchString = query.GetQueryString();
|
||||
var episodeSearchUrl = string.Format(SearchUrl, HttpUtility.UrlEncode(searchString));
|
||||
var results = await RequestStringWithCookiesAndRetry(episodeSearchUrl);
|
||||
|
||||
var searchUrl = SearchUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
queryCollection.Add("release_type", "both");
|
||||
queryCollection.Add("searchtags", "");
|
||||
queryCollection.Add("tags_type", "0");
|
||||
queryCollection.Add("order_by", "s3");
|
||||
queryCollection.Add("order_way", "desc");
|
||||
queryCollection.Add("torrent_preset", "all");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
queryCollection.Add("searchstr", searchString);
|
||||
}
|
||||
|
||||
foreach (var cat in MapTorznabCapsToTrackers(query))
|
||||
{
|
||||
queryCollection.Add("filter_cat["+cat+"]", "1");
|
||||
}
|
||||
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
try
|
||||
{
|
||||
CQ dom = results.Content;
|
||||
@@ -96,6 +147,16 @@ namespace Jackett.Indexers
|
||||
release.Seeders = ParseUtil.CoerceInt(qRow.Children().ElementAt(6).InnerText.Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(qRow.Children().ElementAt(7).InnerText.Trim()) + release.Seeders;
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(6)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
release.DownloadVolumeFactor = 0; // ratioless
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
var qCat = qRow.Find("a[href^=\"torrents.php?filter_cat\"]");
|
||||
var cat = qCat.Attr("href").Split('[')[1].Split(']')[0];
|
||||
release.Category = MapTrackerCatToNewznab(cat);
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
212
src/Jackett/Indexers/TorrentSyndikat.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class TorrentSyndikat : BaseIndexer, IIndexer
|
||||
{
|
||||
private string SearchUrl { get { return SiteLink + "browse.php"; } }
|
||||
private string LoginUrl { get { return SiteLink + "eing2.php"; } }
|
||||
private string CaptchaUrl { get { return SiteLink + "simpleCaptcha.php?numImages=1"; } }
|
||||
TimeZoneInfo germanyTz = TimeZoneInfo.CreateCustomTimeZone("W. Europe Standard Time", new TimeSpan(1, 0, 0), "W. Europe Standard Time", "W. Europe Standard Time");
|
||||
|
||||
new ConfigurationDataBasicLoginWithRSSAndDisplay configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLoginWithRSSAndDisplay)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public TorrentSyndikat(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps)
|
||||
: base(name: "Torrent-Syndikat",
|
||||
description: "A German general tracker",
|
||||
link: "https://torrent-syndikat.org/",
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLoginWithRSSAndDisplay())
|
||||
{
|
||||
this.configData.DisplayText.Value = "Only the results from the first search result page are shown, adjust your profile settings to show the maximum.";
|
||||
this.configData.DisplayText.Name = "Notice";
|
||||
|
||||
AddCategoryMapping(2, TorznabCatType.PC); // Apps / Windows
|
||||
AddCategoryMapping(13, TorznabCatType.PC); // Apps / Linux
|
||||
AddCategoryMapping(4, TorznabCatType.PCMac); // Apps / Mac
|
||||
AddCategoryMapping(6, TorznabCatType.PC); // Apps / Misc
|
||||
|
||||
AddCategoryMapping(12, TorznabCatType.PCGames); // Spiele / PC
|
||||
AddCategoryMapping(8, TorznabCatType.ConsolePSP); // Spiele / PSX/PSP
|
||||
AddCategoryMapping(7, TorznabCatType.ConsoleWii); // Spiele / Wii
|
||||
AddCategoryMapping(32, TorznabCatType.ConsoleXbox); // Spiele / XBOX
|
||||
AddCategoryMapping(41, TorznabCatType.ConsoleNDS); // Spiele / Nintendo DS
|
||||
|
||||
AddCategoryMapping(22, TorznabCatType.Movies3D); // Filme / 3D
|
||||
AddCategoryMapping(3, TorznabCatType.MoviesBluRay); // Filme / BluRay
|
||||
AddCategoryMapping(11, TorznabCatType.MoviesOther); // Filme / REMUX
|
||||
AddCategoryMapping(42, TorznabCatType.MoviesHD); // Filme / 2160p
|
||||
AddCategoryMapping(9, TorznabCatType.MoviesHD); // Filme / 1080p
|
||||
AddCategoryMapping(20, TorznabCatType.MoviesHD); // Filme / 720p
|
||||
AddCategoryMapping(21, TorznabCatType.MoviesDVD); // Filme / DVD
|
||||
AddCategoryMapping(10, TorznabCatType.MoviesSD); // Filme / SD
|
||||
AddCategoryMapping(31, TorznabCatType.MoviesOther); // Filme / Anime
|
||||
AddCategoryMapping(37, TorznabCatType.MoviesForeign); // Filme / Englisch
|
||||
|
||||
AddCategoryMapping(16, TorznabCatType.TVHD); // TV / Serien/HD
|
||||
AddCategoryMapping(15, TorznabCatType.TVSD); // TV / Serien/SD
|
||||
AddCategoryMapping(44, TorznabCatType.TVHD); // TV / Packs/UHD
|
||||
AddCategoryMapping(23, TorznabCatType.TVHD); // TV / Packs/HD
|
||||
AddCategoryMapping(27, TorznabCatType.TVSD); // TV / Packs/SD
|
||||
AddCategoryMapping(28, TorznabCatType.TVDocumentary); // TV / Dokus/SD
|
||||
AddCategoryMapping(29, TorznabCatType.TVDocumentary); // TV / Dokus/HD
|
||||
AddCategoryMapping(30, TorznabCatType.TVSport); // TV / Sport
|
||||
AddCategoryMapping(40, TorznabCatType.TVAnime); // TV / Anime
|
||||
AddCategoryMapping(36, TorznabCatType.TVFOREIGN); // TV / Englisch
|
||||
|
||||
AddCategoryMapping(24, TorznabCatType.AudioLossless); // Audio / FLAC
|
||||
AddCategoryMapping(25, TorznabCatType.AudioMP3); // Audio / MP3
|
||||
AddCategoryMapping(35, TorznabCatType.AudioOther); // Audio / Other
|
||||
AddCategoryMapping(26, TorznabCatType.Audio); // Audio / Packs
|
||||
AddCategoryMapping(18, TorznabCatType.AudioAudiobook); // Audio / aBooks
|
||||
AddCategoryMapping(33, TorznabCatType.AudioVideo); // Audio / Videos
|
||||
|
||||
AddCategoryMapping(17, TorznabCatType.Books); // Misc / eBooks
|
||||
AddCategoryMapping(5, TorznabCatType.PCPhoneOther); // Misc / Mobile
|
||||
AddCategoryMapping(39, TorznabCatType.Other); // Misc / Bildung
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
var result1 = await RequestStringWithCookies(CaptchaUrl);
|
||||
var json1 = JObject.Parse(result1.Content);
|
||||
var captchaSelection = json1["images"][0]["hash"];
|
||||
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "captchaSelection", (string)captchaSelection },
|
||||
{ "submitme", "X" }
|
||||
};
|
||||
|
||||
var result2 = await RequestLoginAndFollowRedirect(LoginUrl, pairs, result1.Cookies, true, null, null, true);
|
||||
|
||||
await ConfigureIfOK(result2.Cookies, result2.Content.Contains("/logout.php"), () =>
|
||||
{
|
||||
var errorMessage = result2.Content;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
List<ReleaseInfo> releases = new List<ReleaseInfo>();
|
||||
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = SearchUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
queryCollection.Add("searchin", "title");
|
||||
queryCollection.Add("incldead", "1");
|
||||
queryCollection.Add("rel_type", "0"); // Alle
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
// use AND+wildcard operator to avoid getting to many useless results
|
||||
var searchStringArray = Regex.Split(searchString.Trim(), "[ _.-]+", RegexOptions.Compiled).ToList();
|
||||
searchStringArray = searchStringArray.Where(x => x.Length >= 3).ToList(); // remove words with less than 3 characters
|
||||
searchStringArray = searchStringArray.Select(x => "+" + x + "*").ToList(); // add AND operators+wildcards
|
||||
var searchStringFinal = String.Join(" ", searchStringArray);
|
||||
queryCollection.Add("search", searchStringFinal);
|
||||
}
|
||||
|
||||
foreach (var cat in MapTorznabCapsToTrackers(query))
|
||||
{
|
||||
queryCollection.Add("c" + cat, "1");
|
||||
}
|
||||
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
try
|
||||
{
|
||||
CQ dom = results.Content;
|
||||
var rows = dom["table.torrent_table > tbody > tr"];
|
||||
foreach (var row in rows.Skip(1))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 96*60*60;
|
||||
|
||||
var qRow = row.Cq();
|
||||
|
||||
var catStr = row.ChildElements.ElementAt(0).FirstElementChild.GetAttribute("href").Split('=')[1];
|
||||
release.Category = MapTrackerCatToNewznab(catStr);
|
||||
|
||||
var qLink = row.ChildElements.ElementAt(2).FirstElementChild.Cq();
|
||||
release.Link = new Uri(SiteLink + qLink.Attr("href"));
|
||||
var torrentId = qLink.Attr("href").Split('=').Last();
|
||||
|
||||
var descCol = row.ChildElements.ElementAt(1);
|
||||
var qCommentLink = descCol.FirstElementChild.Cq();
|
||||
var torrentTag = descCol.Cq().Find("span.torrent-tag");
|
||||
var torrentTags = torrentTag.Elements.Select(x => x.InnerHTML).ToList();
|
||||
release.Title = qCommentLink.Text();
|
||||
release.Description = String.Join(", ", torrentTags);
|
||||
release.Comments = new Uri(SiteLink + "/" + qCommentLink.Attr("href").Replace("&hit=1", ""));
|
||||
release.Guid = release.Comments;
|
||||
|
||||
var torrent_details = descCol.ChildElements.Last();
|
||||
var dateStr = torrent_details.ChildNodes.ElementAt(torrent_details.ChildNodes.Length-3).Cq().Text().Replace(" von ", "").Trim();
|
||||
DateTime dateGerman;
|
||||
if (dateStr.StartsWith("Heute "))
|
||||
dateGerman = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) + TimeSpan.Parse(dateStr.Split(' ')[1]);
|
||||
else if (dateStr.StartsWith("Gestern "))
|
||||
dateGerman = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) + TimeSpan.Parse(dateStr.Split(' ')[1]) - TimeSpan.FromDays(1);
|
||||
else
|
||||
dateGerman = DateTime.SpecifyKind(DateTime.ParseExact(dateStr, "dd.MM.yyyy HH:mm", CultureInfo.InvariantCulture), DateTimeKind.Unspecified);
|
||||
|
||||
DateTime pubDateUtc = TimeZoneInfo.ConvertTimeToUtc(dateGerman, germanyTz);
|
||||
release.PublishDate = pubDateUtc.ToLocalTime();
|
||||
|
||||
var sizeStr = row.ChildElements.ElementAt(5).Cq().Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(row.ChildElements.ElementAt(7).Cq().Text());
|
||||
release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(8).Cq().Text()) + release.Seeders;
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(7)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (qRow.Find("span.torrent-tag-free").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
@@ -18,7 +18,7 @@ namespace Jackett.Indexers
|
||||
public class TransmitheNet : BaseIndexer, IIndexer
|
||||
{
|
||||
private string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "torrents.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "torrents.php?action=basic&order_by=time&order_way=desc&search_type=0&taglist=&tags_type=0"; } }
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
@@ -42,6 +42,12 @@ namespace Jackett.Indexers
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
await DoLogin();
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
private async Task DoLogin()
|
||||
{
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
@@ -60,17 +66,23 @@ namespace Jackett.Indexers
|
||||
var errorMessage = messageEl.TextContent.Trim();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var loggedInCheck = await RequestStringWithCookies(SearchUrl);
|
||||
if (!loggedInCheck.Content.Contains("logout.php"))
|
||||
{
|
||||
//Cookie appears to expire after a period of time or logging in to the site via browser
|
||||
await DoLogin();
|
||||
}
|
||||
|
||||
string Url;
|
||||
if (string.IsNullOrEmpty(query.GetQueryString()))
|
||||
Url = SearchUrl;
|
||||
else
|
||||
{
|
||||
Url = $"{SearchUrl}?searchtext={HttpUtility.UrlEncode(query.GetQueryString())}";
|
||||
Url = $"{SearchUrl}&searchtext={HttpUtility.UrlEncode(query.GetQueryString())}";
|
||||
}
|
||||
|
||||
var response = await RequestStringWithCookiesAndRetry(Url);
|
||||
@@ -87,7 +99,7 @@ namespace Jackett.Indexers
|
||||
{
|
||||
var parser = new HtmlParser();
|
||||
var document = parser.Parse(htmlResponse);
|
||||
var rows = document.QuerySelectorAll(".torrent_table > tbody > tr:not(:First-child)");
|
||||
var rows = document.QuerySelectorAll(".torrent_table > tbody > tr[class^='torrent row']");
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
@@ -101,7 +113,10 @@ namespace Jackett.Indexers
|
||||
}
|
||||
else
|
||||
{
|
||||
title = title.Remove(title.LastIndexOf("."));
|
||||
if (title.Length > 5 && title.Substring(title.Length - 5).Contains("."))
|
||||
{
|
||||
title = title.Remove(title.LastIndexOf("."));
|
||||
}
|
||||
}
|
||||
|
||||
release.Title = title;
|
||||
|
@@ -131,6 +131,7 @@ namespace Jackett.Indexers
|
||||
if (rssPage.Content.EndsWith("\0")) {
|
||||
rssPage.Content = rssPage.Content.Substring(0, rssPage.Content.Length - 1);
|
||||
}
|
||||
rssPage.Content = rssPage.Content.Replace("\0x10", "").Replace("\0x07", "");
|
||||
var rssDoc = XDocument.Parse(rssPage.Content);
|
||||
|
||||
foreach (var item in rssDoc.Descendants("item"))
|
||||
@@ -255,6 +256,19 @@ namespace Jackett.Indexers
|
||||
if (release.Category == 0)
|
||||
release.Category = 5030;
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(6)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (qRow.Find("img[alt*=\"Free Torrent\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
if (qRow.Find("img[alt*=\"x2 Torrent\"]").Length >= 1)
|
||||
release.UploadVolumeFactor = 2;
|
||||
else
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|