mirror of
https://github.com/Jackett/Jackett.git
synced 2025-09-13 23:44:10 +02:00
Compare commits
57 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a89246c9a7 | ||
![]() |
69b01c34f8 | ||
![]() |
a41bf6ebe4 | ||
![]() |
5bf224d84d | ||
![]() |
03af2a6d1d | ||
![]() |
aee7bba3ce | ||
![]() |
29b1304967 | ||
![]() |
81029b3019 | ||
![]() |
62caeb3f4d | ||
![]() |
fba3e1922f | ||
![]() |
0aacf6344d | ||
![]() |
9d3d8ea18d | ||
![]() |
4da3921e21 | ||
![]() |
fed41d5b68 | ||
![]() |
15841e26a8 | ||
![]() |
22a5f46276 | ||
![]() |
eb5642f293 | ||
![]() |
98297b2d85 | ||
![]() |
d900ac065f | ||
![]() |
469620d88b | ||
![]() |
461594e336 | ||
![]() |
63f1870319 | ||
![]() |
ba9710cd7c | ||
![]() |
44c814417c | ||
![]() |
d1290fc226 | ||
![]() |
fa88fa8af8 | ||
![]() |
318b3667ff | ||
![]() |
7b0adc7e45 | ||
![]() |
d27ab11d8c | ||
![]() |
5b6c9fee49 | ||
![]() |
594959c858 | ||
![]() |
5b570b5cec | ||
![]() |
875be5dde9 | ||
![]() |
ffd1600226 | ||
![]() |
07ef45b637 | ||
![]() |
24f7fa461d | ||
![]() |
8ebeb2bb17 | ||
![]() |
fe3fcda356 | ||
![]() |
b689b4581e | ||
![]() |
623da8f70d | ||
![]() |
6afaf39d5a | ||
![]() |
55bd19dc45 | ||
![]() |
0979b0c86d | ||
![]() |
f4c8677091 | ||
![]() |
1123e9d101 | ||
![]() |
9a39fcc310 | ||
![]() |
e8fc2c758f | ||
![]() |
b8cdf9f929 | ||
![]() |
89c860a16d | ||
![]() |
ee5255b24a | ||
![]() |
674e6e37ad | ||
![]() |
082e761d0d | ||
![]() |
6e8726c197 | ||
![]() |
3a8ed21d75 | ||
![]() |
eda0205dcd | ||
![]() |
bd250ff971 | ||
![]() |
1c63e9444d |
@@ -123,7 +123,7 @@ dotnet_style_prefer_compound_assignment=true:suggestion
|
||||
###############################
|
||||
# Style Definitions
|
||||
dotnet_naming_style.pascal_case_style.capitalization=pascal_case
|
||||
# Use PascalCase for constant fields
|
||||
# Use PascalCase for constant fields
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity=warning
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols=constant_fields
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.style=pascal_case_style
|
||||
@@ -161,11 +161,11 @@ dotnet_naming_rule.asyncmethods_should_be_ends_with_async.style = ends_with_asyn
|
||||
|
||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.interface.required_modifiers =
|
||||
dotnet_naming_symbols.interface.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected
|
||||
dotnet_naming_symbols.private_or_internal_field.required_modifiers =
|
||||
dotnet_naming_symbols.private_or_internal_field.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.private_or_internal_static_field.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_or_internal_static_field.applicable_accessibilities = internal, private, private_protected
|
||||
@@ -173,11 +173,11 @@ dotnet_naming_symbols.private_or_internal_static_field.required_modifiers = stat
|
||||
|
||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.types.required_modifiers =
|
||||
dotnet_naming_symbols.types.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.asyncmethods.applicable_kinds = delegate, method, local_function
|
||||
dotnet_naming_symbols.asyncmethods.applicable_accessibilities = *
|
||||
@@ -185,29 +185,29 @@ dotnet_naming_symbols.asyncmethods.required_modifiers = async
|
||||
|
||||
# Naming styles
|
||||
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
dotnet_naming_style.pascal_case.required_suffix =
|
||||
dotnet_naming_style.pascal_case.word_separator =
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
dotnet_naming_style.pascal_case.required_suffix =
|
||||
dotnet_naming_style.pascal_case.word_separator =
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||
dotnet_naming_style.begins_with_i.required_suffix =
|
||||
dotnet_naming_style.begins_with_i.word_separator =
|
||||
dotnet_naming_style.begins_with_i.required_suffix =
|
||||
dotnet_naming_style.begins_with_i.word_separator =
|
||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.private_or_internal_static_with_prefix.required_prefix = s_
|
||||
dotnet_naming_style.private_or_internal_static_with_prefix.required_suffix =
|
||||
dotnet_naming_style.private_or_internal_static_with_prefix.word_separator =
|
||||
dotnet_naming_style.private_or_internal_static_with_prefix.required_prefix = _
|
||||
dotnet_naming_style.private_or_internal_static_with_prefix.required_suffix =
|
||||
dotnet_naming_style.private_or_internal_static_with_prefix.word_separator =
|
||||
dotnet_naming_style.private_or_internal_static_with_prefix.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.private_prefix.required_prefix = _
|
||||
dotnet_naming_style.private_prefix.required_suffix =
|
||||
dotnet_naming_style.private_prefix.word_separator =
|
||||
dotnet_naming_style.private_prefix.required_suffix =
|
||||
dotnet_naming_style.private_prefix.word_separator =
|
||||
dotnet_naming_style.private_prefix.capitalization = camel_case
|
||||
|
||||
dotnet_naming_style.ends_with_async.required_prefix =
|
||||
dotnet_naming_style.ends_with_async.required_prefix =
|
||||
dotnet_naming_style.ends_with_async.required_suffix = Async
|
||||
dotnet_naming_style.ends_with_async.word_separator =
|
||||
dotnet_naming_style.ends_with_async.word_separator =
|
||||
dotnet_naming_style.ends_with_async.capitalization = pascal_case
|
||||
|
||||
###############################
|
||||
|
19
.gitattributes
vendored
19
.gitattributes
vendored
@@ -1,16 +1,13 @@
|
||||
# Set the default behavior, in case people don't have core.autocrlf set.
|
||||
* text=auto
|
||||
|
||||
# Explicitly declare text files you want to always be normalized and converted
|
||||
# to native line endings on checkout.
|
||||
|
||||
# Declare files that will always have LF line endings on checkout.
|
||||
*.yml text eol=lf
|
||||
*.sh text eol=lf
|
||||
|
||||
# Declare files that will always have CRLF line endings on checkout.
|
||||
*.cs text eol=crlf
|
||||
* text eol=lf
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
*.png binary
|
||||
*.eot binary
|
||||
*.ico binary
|
||||
*.jpg binary
|
||||
*.otf binary
|
||||
*.png binary
|
||||
*.ttf binary
|
||||
*.woff binary
|
||||
*.woff2 binary
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -195,9 +195,6 @@ FakesAssemblies/
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
/tools
|
||||
/BuildOutput
|
||||
/Artifacts
|
||||
/TestResults
|
||||
*.DS_Store
|
||||
.idea/
|
||||
launchSettings.json
|
||||
|
11
README.md
11
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://github.com/Jackett/Jackett/issues)
|
||||
[](https://github.com/Jackett/Jackett/pulls)
|
||||
[](https://dev.azure.com/Jackett-project/Jackett/_build/latest?definitionId=1&branchName=master)
|
||||
[](https://dev.azure.com/jackett/jackett/_build/latest?definitionId=1&branchName=master)
|
||||
[](https://github.com/Jackett/Jackett/releases/latest)
|
||||
[](https://hub.docker.com/r/linuxserver/jackett/)
|
||||
[](https://discord.gg/J865QuA)
|
||||
@@ -18,7 +18,7 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
|
||||
|
||||
#### Supported Systems
|
||||
* Windows 7SP1 or greater using .NET 4.6.1 or above [Download here](https://www.microsoft.com/net/framework/versions/net461)
|
||||
* Windows 7SP1 or greater
|
||||
* Linux [supported operating systems here](https://github.com/dotnet/core/blob/master/release-notes/2.1/2.1-supported-os.md#linux)
|
||||
* macOS 10.13 or greater
|
||||
|
||||
@@ -121,6 +121,7 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* Torlock
|
||||
* TOROS
|
||||
* Torrent Downloads (TD)
|
||||
* Torrent Oyun indir
|
||||
* torrent-pirat
|
||||
* Torrent4You
|
||||
* Torrent9
|
||||
@@ -136,7 +137,6 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* Torrents.csv
|
||||
* Torrentv
|
||||
* TorrentView
|
||||
* TorrentWal
|
||||
* Torrentz2
|
||||
* Underverse
|
||||
* UnionDHT
|
||||
@@ -152,7 +152,6 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* AlexFilm
|
||||
* AniDUB
|
||||
* ArenaBG
|
||||
* BaibaKo
|
||||
* BookTracker
|
||||
* CasStudioTV
|
||||
* Crazy's Corner
|
||||
@@ -221,6 +220,7 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* Audiobook Torrents (ABT)
|
||||
* Avistaz (AsiaTorrents)
|
||||
* Awesome-HD (AHD)
|
||||
* BaibaKo
|
||||
* BIGTorrent
|
||||
* BIT-HDTV
|
||||
* BJ-Share (BJ)
|
||||
@@ -284,6 +284,7 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* EfectoDoppler
|
||||
* Elite-Tracker
|
||||
* Empornium (EMP)
|
||||
* EpubLibre
|
||||
* Ethor.net (Thor's Land)
|
||||
* ExoticaZ (YourExotic)
|
||||
* ExtremeTorrents
|
||||
@@ -345,6 +346,7 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* JPopsuki
|
||||
* Kapaki
|
||||
* Karagarga
|
||||
* LastFiles
|
||||
* Le Saloon
|
||||
* LeChaudron
|
||||
* LeagueHD
|
||||
@@ -481,6 +483,7 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* TurkTorrent (TT)
|
||||
* Twilight Torrents
|
||||
* Twilights Zoom
|
||||
* U-Torrents
|
||||
* U2 (U2分享園@動漫花園)
|
||||
* UHDBits
|
||||
* UnionGang
|
||||
|
@@ -1,7 +1,7 @@
|
||||
name: $(majorVersion).$(minorVersion).$(patchVersion)
|
||||
variables:
|
||||
majorVersion: 0
|
||||
minorVersion: 13
|
||||
minorVersion: 14
|
||||
patchVersion: $[counter(variables['minorVersion'], 1)] #this will reset when we bump minor
|
||||
jackettVersion: $(majorVersion).$(minorVersion).$(patchVersion)
|
||||
buildConfiguration: Release
|
||||
@@ -9,6 +9,12 @@ variables:
|
||||
netCoreSdkVersion: 3.1.x
|
||||
system.debug: true
|
||||
|
||||
pr:
|
||||
autoCancel: true
|
||||
|
||||
trigger:
|
||||
batch: true
|
||||
|
||||
stages:
|
||||
- stage: BuildJackett
|
||||
displayName: Create Binaries
|
||||
@@ -353,17 +359,18 @@ stages:
|
||||
patterns: '**/Jackett*'
|
||||
path: $(Build.ArtifactStagingDirectory)
|
||||
|
||||
- task: GitHubRelease@0
|
||||
- task: GitHubRelease@1
|
||||
displayName: Create Github release
|
||||
inputs:
|
||||
gitHubConnection: github.com_jackett
|
||||
repositoryName: '$(Build.Repository.Name)'
|
||||
action: create
|
||||
target: $(Build.SourceVersion)
|
||||
tagSource: manual
|
||||
tagSource: userSpecifiedTag
|
||||
tag: v$(Build.BuildNumber)
|
||||
title: v$(Build.BuildNumber)
|
||||
assets: $(Build.ArtifactStagingDirectory)/drop/*
|
||||
assetUploadMode: replace
|
||||
isDraft: true
|
||||
addChangeLog: true
|
||||
compareWith: lastNonDraftRelease
|
||||
@@ -373,17 +380,18 @@ stages:
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: |
|
||||
$json = Invoke-WebRequest 'https://dev.azure.com/jackett-project/jackett/_apis/build/builds/$(Build.BuildId)/logs?api-version=5.0' | ConvertFrom-Json
|
||||
$json = Invoke-WebRequest 'https://dev.azure.com/jackett/jackett/_apis/build/builds/$(Build.BuildId)/logs?api-version=5.0' | ConvertFrom-Json
|
||||
$lastTwoLogUrls = $json.value[-1..-2].url
|
||||
foreach($logUrl in $lastTwoLogUrls)
|
||||
{
|
||||
Write-Output $logUrl
|
||||
$logText = Invoke-WebRequest $logUrl
|
||||
if ($logText -like '*: GitHub Release*')
|
||||
if ($logText -like '*Creating a release for tag:*')
|
||||
{
|
||||
$successCount = (Select-String "Uploaded file successfully:" -InputObject $logText -AllMatches).Matches.Count
|
||||
$failureCount = (Select-String "Duplicate asset found:" -InputObject $logText -AllMatches).Matches.Count
|
||||
Write-Host "Success count is: $successCount and failure count is: $failureCount"
|
||||
$logInspect = ($logText -split "Creating a release for tag:")[-1]
|
||||
$successCount = (Select-String "Uploaded file successfully:" -InputObject $logInspect -AllMatches).Matches.Count
|
||||
$failureCount = (Select-String "Duplicate asset found:" -InputObject $logInspect -AllMatches).Matches.Count
|
||||
Write-Output "Success count is: $successCount and failure count is: $failureCount"
|
||||
if (($successCount -ne 7) -or ($failureCount -ne 0)) { Write-Host "##vso[task.complete result=Failed;]DONE" }
|
||||
}
|
||||
}
|
||||
|
@@ -9,6 +9,10 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
// ReSharper disable NotAccessedField.Global
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
namespace DateTimeRoutines
|
||||
{
|
||||
/// <summary>
|
||||
@@ -21,11 +25,11 @@ namespace DateTimeRoutines
|
||||
/// <summary>
|
||||
/// Amount of seconds elapsed between 1970-01-01 00:00:00 and the date-time.
|
||||
/// </summary>
|
||||
/// <param name="date_time">date-time</param>
|
||||
/// <param name="dateTime">date-time</param>
|
||||
/// <returns>seconds</returns>
|
||||
public static uint GetSecondsSinceUnixEpoch(this DateTime date_time)
|
||||
public static uint GetSecondsSinceUnixEpoch(this DateTime dateTime)
|
||||
{
|
||||
var t = date_time - new DateTime(1970, 1, 1);
|
||||
var t = dateTime - new DateTime(1970, 1, 1);
|
||||
var ss = (int)t.TotalSeconds;
|
||||
if (ss < 0)
|
||||
return 0;
|
||||
@@ -44,19 +48,19 @@ namespace DateTimeRoutines
|
||||
/// <summary>
|
||||
/// Index of first char of a date substring found in the string
|
||||
/// </summary>
|
||||
public readonly int IndexOfDate = -1;
|
||||
public readonly int IndexOfDate;
|
||||
/// <summary>
|
||||
/// Length a date substring found in the string
|
||||
/// </summary>
|
||||
public readonly int LengthOfDate = -1;
|
||||
public readonly int LengthOfDate;
|
||||
/// <summary>
|
||||
/// Index of first char of a time substring found in the string
|
||||
/// </summary>
|
||||
public readonly int IndexOfTime = -1;
|
||||
public readonly int IndexOfTime;
|
||||
/// <summary>
|
||||
/// Length of a time substring found in the string
|
||||
/// </summary>
|
||||
public readonly int LengthOfTime = -1;
|
||||
public readonly int LengthOfTime;
|
||||
/// <summary>
|
||||
/// DateTime found in the string
|
||||
/// </summary>
|
||||
@@ -82,45 +86,45 @@ namespace DateTimeRoutines
|
||||
/// </summary>
|
||||
public DateTime UtcDateTime;
|
||||
|
||||
internal ParsedDateTime(int index_of_date, int length_of_date, int index_of_time, int length_of_time, DateTime date_time)
|
||||
internal ParsedDateTime(int indexOfDate, int lengthOfDate, int indexOfTime, int lengthOfTime, DateTime dateTime)
|
||||
{
|
||||
IndexOfDate = index_of_date;
|
||||
LengthOfDate = length_of_date;
|
||||
IndexOfTime = index_of_time;
|
||||
LengthOfTime = length_of_time;
|
||||
DateTime = date_time;
|
||||
IsDateFound = index_of_date > -1;
|
||||
IsTimeFound = index_of_time > -1;
|
||||
IndexOfDate = indexOfDate;
|
||||
LengthOfDate = lengthOfDate;
|
||||
IndexOfTime = indexOfTime;
|
||||
LengthOfTime = lengthOfTime;
|
||||
DateTime = dateTime;
|
||||
IsDateFound = indexOfDate > -1;
|
||||
IsTimeFound = indexOfTime > -1;
|
||||
UtcOffset = new TimeSpan(25, 0, 0);
|
||||
IsUtcOffsetFound = false;
|
||||
UtcDateTime = new DateTime(1, 1, 1);
|
||||
}
|
||||
|
||||
internal ParsedDateTime(int index_of_date, int length_of_date, int index_of_time, int length_of_time, DateTime date_time, TimeSpan utc_offset)
|
||||
internal ParsedDateTime(int indexOfDate, int lengthOfDate, int indexOfTime, int lengthOfTime, DateTime dateTime, TimeSpan utcOffset)
|
||||
{
|
||||
IndexOfDate = index_of_date;
|
||||
LengthOfDate = length_of_date;
|
||||
IndexOfTime = index_of_time;
|
||||
LengthOfTime = length_of_time;
|
||||
DateTime = date_time;
|
||||
IsDateFound = index_of_date > -1;
|
||||
IsTimeFound = index_of_time > -1;
|
||||
UtcOffset = utc_offset;
|
||||
IsUtcOffsetFound = Math.Abs(utc_offset.TotalHours) < 12;
|
||||
IndexOfDate = indexOfDate;
|
||||
LengthOfDate = lengthOfDate;
|
||||
IndexOfTime = indexOfTime;
|
||||
LengthOfTime = lengthOfTime;
|
||||
DateTime = dateTime;
|
||||
IsDateFound = indexOfDate > -1;
|
||||
IsTimeFound = indexOfTime > -1;
|
||||
UtcOffset = utcOffset;
|
||||
IsUtcOffsetFound = Math.Abs(utcOffset.TotalHours) < 12;
|
||||
if (!IsUtcOffsetFound)
|
||||
UtcDateTime = new DateTime(1, 1, 1);
|
||||
else
|
||||
{
|
||||
if (index_of_date < 0)//to avoid negative date exception when date is undefined
|
||||
if (indexOfDate < 0)//to avoid negative date exception when date is undefined
|
||||
{
|
||||
var ts = date_time.TimeOfDay + utc_offset;
|
||||
var ts = dateTime.TimeOfDay + utcOffset;
|
||||
if (ts < new TimeSpan(0))
|
||||
UtcDateTime = new DateTime(1, 1, 2) + ts;
|
||||
else
|
||||
UtcDateTime = new DateTime(1, 1, 1) + ts;
|
||||
}
|
||||
else
|
||||
UtcDateTime = date_time + utc_offset;
|
||||
UtcDateTime = dateTime + utcOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,7 +133,7 @@ namespace DateTimeRoutines
|
||||
/// Date that is accepted in the following cases:
|
||||
/// - no date was parsed by TryParseDateOrTime();
|
||||
/// - no year was found by TryParseDate();
|
||||
/// It is ignored if DefaultDateIsNow = true was set after DefaultDate
|
||||
/// It is ignored if DefaultDateIsNow = true was set after DefaultDate
|
||||
/// </summary>
|
||||
public static DateTime DefaultDate
|
||||
{
|
||||
@@ -138,13 +142,7 @@ namespace DateTimeRoutines
|
||||
_DefaultDate = value;
|
||||
DefaultDateIsNow = false;
|
||||
}
|
||||
get
|
||||
{
|
||||
if (DefaultDateIsNow)
|
||||
return DateTime.Now;
|
||||
else
|
||||
return _DefaultDate;
|
||||
}
|
||||
get => DefaultDateIsNow ? DateTime.Now : _DefaultDate;
|
||||
}
|
||||
|
||||
private static DateTime _DefaultDate = DateTime.Now;
|
||||
@@ -157,16 +155,17 @@ namespace DateTimeRoutines
|
||||
/// <summary>
|
||||
/// Defines default date-time format.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum DateTimeFormat
|
||||
{
|
||||
/// <summary>
|
||||
/// month number goes before day number
|
||||
/// </summary>
|
||||
USA_DATE,
|
||||
UsaDate,
|
||||
/// <summary>
|
||||
/// day number goes before month number
|
||||
/// </summary>
|
||||
UK_DATE,
|
||||
UkDate,
|
||||
///// <summary>
|
||||
///// time is specifed through AM or PM
|
||||
///// </summary>
|
||||
@@ -178,79 +177,79 @@ namespace DateTimeRoutines
|
||||
#region parsing derived methods for DateTime output
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find date and time within the passed string and return it as DateTime structure.
|
||||
/// Tries to find date and time within the passed string and return it as DateTime structure.
|
||||
/// </summary>
|
||||
/// <param name="str">string that contains date and/or time</param>
|
||||
/// <param name="default_format">format to be used preferably in ambivalent instances</param>
|
||||
/// <param name="date_time">parsed date-time output</param>
|
||||
/// <param name="defaultFormat">format to be used preferably in ambivalent instances</param>
|
||||
/// <param name="dateTime">parsed date-time output</param>
|
||||
/// <returns>true if both date and time were found, else false</returns>
|
||||
public static bool TryParseDateTime(this string str, DateTimeFormat default_format, out DateTime date_time)
|
||||
public static bool TryParseDateTime(this string str, DateTimeFormat defaultFormat, out DateTime dateTime)
|
||||
{
|
||||
if (!TryParseDateTime(str, default_format, out ParsedDateTime parsed_date_time))
|
||||
if (!TryParseDateTime(str, defaultFormat, out ParsedDateTime parsedDateTime))
|
||||
{
|
||||
date_time = new DateTime(1, 1, 1);
|
||||
dateTime = new DateTime(1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
date_time = parsed_date_time.DateTime;
|
||||
dateTime = parsedDateTime.DateTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find date and/or time within the passed string and return it as DateTime structure.
|
||||
/// Tries to find date and/or time within the passed string and return it as DateTime structure.
|
||||
/// If only date was found, time in the returned DateTime is always 0:0:0.
|
||||
/// If only time was found, date in the returned DateTime is DefaultDate.
|
||||
/// </summary>
|
||||
/// <param name="str">string that contains date and(or) time</param>
|
||||
/// <param name="default_format">format to be used preferably in ambivalent instances</param>
|
||||
/// <param name="date_time">parsed date-time output</param>
|
||||
/// <param name="defaultFormat">format to be used preferably in ambivalent instances</param>
|
||||
/// <param name="dateTime">parsed date-time output</param>
|
||||
/// <returns>true if date and/or time was found, else false</returns>
|
||||
public static bool TryParseDateOrTime(this string str, DateTimeFormat default_format, out DateTime date_time)
|
||||
public static bool TryParseDateOrTime(this string str, DateTimeFormat defaultFormat, out DateTime dateTime)
|
||||
{
|
||||
if (!TryParseDateOrTime(str, default_format, out ParsedDateTime parsed_date_time))
|
||||
if (!TryParseDateOrTime(str, defaultFormat, out ParsedDateTime parsedDateTime))
|
||||
{
|
||||
date_time = new DateTime(1, 1, 1);
|
||||
dateTime = new DateTime(1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
date_time = parsed_date_time.DateTime;
|
||||
dateTime = parsedDateTime.DateTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find time within the passed string and return it as DateTime structure.
|
||||
/// Tries to find time within the passed string and return it as DateTime structure.
|
||||
/// It recognizes only time while ignoring date, so date in the returned DateTime is always 1/1/1.
|
||||
/// </summary>
|
||||
/// <param name="str">string that contains time</param>
|
||||
/// <param name="default_format">format to be used preferably in ambivalent instances</param>
|
||||
/// <param name="defaultFormat">format to be used preferably in ambivalent instances</param>
|
||||
/// <param name="time">parsed time output</param>
|
||||
/// <returns>true if time was found, else false</returns>
|
||||
public static bool TryParseTime(this string str, DateTimeFormat default_format, out DateTime time)
|
||||
public static bool TryParseTime(this string str, DateTimeFormat defaultFormat, out DateTime time)
|
||||
{
|
||||
if (!TryParseTime(str, default_format, out var parsed_time, null))
|
||||
if (!TryParseTime(str, defaultFormat, out var parsedTime, null))
|
||||
{
|
||||
time = new DateTime(1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
time = parsed_time.DateTime;
|
||||
time = parsedTime.DateTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find date within the passed string and return it as DateTime structure.
|
||||
/// Tries to find date within the passed string and return it as DateTime structure.
|
||||
/// It recognizes only date while ignoring time, so time in the returned DateTime is always 0:0:0.
|
||||
/// If year of the date was not found then it accepts the current year.
|
||||
/// If year of the date was not found then it accepts the current year.
|
||||
/// </summary>
|
||||
/// <param name="str">string that contains date</param>
|
||||
/// <param name="default_format">format to be used preferably in ambivalent instances</param>
|
||||
/// <param name="defaultFormat">format to be used preferably in ambivalent instances</param>
|
||||
/// <param name="date">parsed date output</param>
|
||||
/// <returns>true if date was found, else false</returns>
|
||||
public static bool TryParseDate(this string str, DateTimeFormat default_format, out DateTime date)
|
||||
public static bool TryParseDate(this string str, DateTimeFormat defaultFormat, out DateTime date)
|
||||
{
|
||||
if (!TryParseDate(str, default_format, out ParsedDateTime parsed_date))
|
||||
if (!TryParseDate(str, defaultFormat, out ParsedDateTime parsedDate))
|
||||
{
|
||||
date = new DateTime(1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
date = parsed_date.DateTime;
|
||||
date = parsedDate.DateTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -259,69 +258,69 @@ namespace DateTimeRoutines
|
||||
#region parsing derived methods for ParsedDateTime output
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find date and time within the passed string and return it as ParsedDateTime object.
|
||||
/// Tries to find date and time within the passed string and return it as ParsedDateTime object.
|
||||
/// </summary>
|
||||
/// <param name="str">string that contains date-time</param>
|
||||
/// <param name="default_format">format to be used preferably in ambivalent instances</param>
|
||||
/// <param name="parsed_date_time">parsed date-time output</param>
|
||||
/// <param name="defaultFormat">format to be used preferably in ambivalent instances</param>
|
||||
/// <param name="parsedDateTime">parsed date-time output</param>
|
||||
/// <returns>true if both date and time were found, else false</returns>
|
||||
public static bool TryParseDateTime(this string str, DateTimeFormat default_format, out ParsedDateTime parsed_date_time)
|
||||
public static bool TryParseDateTime(this string str, DateTimeFormat defaultFormat, out ParsedDateTime parsedDateTime)
|
||||
{
|
||||
if (DateTimeRoutines.TryParseDateOrTime(str, default_format, out parsed_date_time)
|
||||
&& parsed_date_time.IsDateFound
|
||||
&& parsed_date_time.IsTimeFound
|
||||
if (TryParseDateOrTime(str, defaultFormat, out parsedDateTime)
|
||||
&& parsedDateTime.IsDateFound
|
||||
&& parsedDateTime.IsTimeFound
|
||||
)
|
||||
return true;
|
||||
|
||||
parsed_date_time = null;
|
||||
parsedDateTime = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find time within the passed string and return it as ParsedDateTime object.
|
||||
/// Tries to find time within the passed string and return it as ParsedDateTime object.
|
||||
/// It recognizes only time while ignoring date, so date in the returned ParsedDateTime is always 1/1/1
|
||||
/// </summary>
|
||||
/// <param name="str">string that contains date-time</param>
|
||||
/// <param name="default_format">format to be used preferably in ambivalent instances</param>
|
||||
/// <param name="parsed_time">parsed date-time output</param>
|
||||
/// <param name="defaultFormat">format to be used preferably in ambivalent instances</param>
|
||||
/// <param name="parsedTime">parsed date-time output</param>
|
||||
/// <returns>true if time was found, else false</returns>
|
||||
public static bool TryParseTime(this string str, DateTimeFormat default_format, out ParsedDateTime parsed_time)
|
||||
=> TryParseTime(str, default_format, out parsed_time, null);
|
||||
public static bool TryParseTime(this string str, DateTimeFormat defaultFormat, out ParsedDateTime parsedTime)
|
||||
=> TryParseTime(str, defaultFormat, out parsedTime, null);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find date and/or time within the passed string and return it as ParsedDateTime object.
|
||||
/// Tries to find date and/or time within the passed string and return it as ParsedDateTime object.
|
||||
/// If only date was found, time in the returned ParsedDateTime is always 0:0:0.
|
||||
/// If only time was found, date in the returned ParsedDateTime is DefaultDate.
|
||||
/// </summary>
|
||||
/// <param name="str">string that contains date-time</param>
|
||||
/// <param name="default_format">format to be used preferably in ambivalent instances</param>
|
||||
/// <param name="parsed_date_time">parsed date-time output</param>
|
||||
/// <param name="defaultFormat">format to be used preferably in ambivalent instances</param>
|
||||
/// <param name="parsedDateTime">parsed date-time output</param>
|
||||
/// <returns>true if date or time was found, else false</returns>
|
||||
public static bool TryParseDateOrTime(this string str, DateTimeFormat default_format, out ParsedDateTime parsed_date_time)
|
||||
public static bool TryParseDateOrTime(this string str, DateTimeFormat defaultFormat, out ParsedDateTime parsedDateTime)
|
||||
{
|
||||
parsed_date_time = null;
|
||||
parsedDateTime = null;
|
||||
|
||||
ParsedDateTime parsed_time;
|
||||
if (!TryParseDate(str, default_format, out
|
||||
ParsedDateTime parsed_date))
|
||||
ParsedDateTime parsedTime;
|
||||
if (!TryParseDate(str, defaultFormat, out
|
||||
ParsedDateTime parsedDate))
|
||||
{
|
||||
if (!TryParseTime(str, default_format, out parsed_time, null))
|
||||
if (!TryParseTime(str, defaultFormat, out parsedTime, null))
|
||||
return false;
|
||||
|
||||
var date_time = new DateTime(DefaultDate.Year, DefaultDate.Month, DefaultDate.Day, parsed_time.DateTime.Hour, parsed_time.DateTime.Minute, parsed_time.DateTime.Second);
|
||||
parsed_date_time = new ParsedDateTime(-1, -1, parsed_time.IndexOfTime, parsed_time.LengthOfTime, date_time, parsed_time.UtcOffset);
|
||||
var dateTime = new DateTime(DefaultDate.Year, DefaultDate.Month, DefaultDate.Day, parsedTime.DateTime.Hour, parsedTime.DateTime.Minute, parsedTime.DateTime.Second);
|
||||
parsedDateTime = new ParsedDateTime(-1, -1, parsedTime.IndexOfTime, parsedTime.LengthOfTime, dateTime, parsedTime.UtcOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!TryParseTime(str, default_format, out parsed_time, parsed_date))
|
||||
if (!TryParseTime(str, defaultFormat, out parsedTime, parsedDate))
|
||||
{
|
||||
var date_time = new DateTime(parsed_date.DateTime.Year, parsed_date.DateTime.Month, parsed_date.DateTime.Day, 0, 0, 0);
|
||||
parsed_date_time = new ParsedDateTime(parsed_date.IndexOfDate, parsed_date.LengthOfDate, -1, -1, date_time);
|
||||
var dateTime = new DateTime(parsedDate.DateTime.Year, parsedDate.DateTime.Month, parsedDate.DateTime.Day, 0, 0, 0);
|
||||
parsedDateTime = new ParsedDateTime(parsedDate.IndexOfDate, parsedDate.LengthOfDate, -1, -1, dateTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
var date_time = new DateTime(parsed_date.DateTime.Year, parsed_date.DateTime.Month, parsed_date.DateTime.Day, parsed_time.DateTime.Hour, parsed_time.DateTime.Minute, parsed_time.DateTime.Second);
|
||||
parsed_date_time = new ParsedDateTime(parsed_date.IndexOfDate, parsed_date.LengthOfDate, parsed_time.IndexOfTime, parsed_time.LengthOfTime, date_time, parsed_time.UtcOffset);
|
||||
var dateTime = new DateTime(parsedDate.DateTime.Year, parsedDate.DateTime.Month, parsedDate.DateTime.Day, parsedTime.DateTime.Hour, parsedTime.DateTime.Minute, parsedTime.DateTime.Second);
|
||||
parsedDateTime = new ParsedDateTime(parsedDate.IndexOfDate, parsedDate.LengthOfDate, parsedTime.IndexOfTime, parsedTime.LengthOfTime, dateTime, parsedTime.UtcOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,42 +336,39 @@ namespace DateTimeRoutines
|
||||
/// It recognizes only time while ignoring date, so date in the returned ParsedDateTime is always 1/1/1
|
||||
/// </summary>
|
||||
/// <param name="str">string that contains date</param>
|
||||
/// <param name="default_format">format to be used preferably in ambivalent instances</param>
|
||||
/// <param name="parsed_time">parsed date-time output</param>
|
||||
/// <param name="parsed_date">ParsedDateTime object if the date was found within this string, else NULL</param>
|
||||
/// <param name="defaultFormat">format to be used preferably in ambivalent instances</param>
|
||||
/// <param name="parsedTime">parsed date-time output</param>
|
||||
/// <param name="parsedDate">ParsedDateTime object if the date was found within this string, else NULL</param>
|
||||
/// <returns>true if time was found, else false</returns>
|
||||
public static bool TryParseTime(this string str, DateTimeFormat default_format, out ParsedDateTime parsed_time, ParsedDateTime parsed_date)
|
||||
public static bool TryParseTime(this string str, DateTimeFormat defaultFormat, out ParsedDateTime parsedTime, ParsedDateTime parsedDate)
|
||||
{
|
||||
parsed_time = null;
|
||||
parsedTime = null;
|
||||
|
||||
string time_zone_r;
|
||||
if (default_format == DateTimeFormat.USA_DATE)
|
||||
time_zone_r = @"(?:\s*(?'time_zone'UTC|GMT|CST|EST))?";
|
||||
else
|
||||
time_zone_r = @"(?:\s*(?'time_zone'UTC|GMT))?";
|
||||
var timeZoneR = defaultFormat == DateTimeFormat.UsaDate ?
|
||||
@"(?:\s*(?'time_zone'UTC|GMT|CST|EST))?" : @"(?:\s*(?'time_zone'UTC|GMT))?";
|
||||
|
||||
Match m;
|
||||
if (parsed_date != null && parsed_date.IndexOfDate > -1)
|
||||
if (parsedDate != null && parsedDate.IndexOfDate > -1)
|
||||
{//look around the found date
|
||||
//look for <date> hh:mm:ss <UTC offset>
|
||||
m = Regex.Match(str.Substring(parsed_date.IndexOfDate + parsed_date.LengthOfDate), @"(?<=^\s*,?\s+|^\s*at\s*|^\s*[T\-]\s*)(?'hour'\d{2})\s*:\s*(?'minute'\d{2})\s*:\s*(?'second'\d{2})\s+(?'offset_sign'[\+\-])(?'offset_hh'\d{2}):?(?'offset_mm'\d{2})(?=$|[^\d\w])", RegexOptions.Compiled);
|
||||
//look for <date> hh:mm:ss <UTC offset>
|
||||
m = Regex.Match(str.Substring(parsedDate.IndexOfDate + parsedDate.LengthOfDate), @"(?<=^\s*,?\s+|^\s*at\s*|^\s*[T\-]\s*)(?'hour'\d{2})\s*:\s*(?'minute'\d{2})\s*:\s*(?'second'\d{2})\s+(?'offset_sign'[\+\-])(?'offset_hh'\d{2}):?(?'offset_mm'\d{2})(?=$|[^\d\w])", RegexOptions.Compiled);
|
||||
if (!m.Success)
|
||||
//look for <date> [h]h:mm[:ss] [PM/AM] [UTC/GMT]
|
||||
m = Regex.Match(str.Substring(parsed_date.IndexOfDate + parsed_date.LengthOfDate), @"(?<=^\s*,?\s+|^\s*at\s*|^\s*[T\-]\s*)(?'hour'\d{1,2})\s*:\s*(?'minute'\d{2})\s*(?::\s*(?'second'\d{2}))?(?:\s*(?'ampm'AM|am|PM|pm))?" + time_zone_r + @"(?=$|[^\d\w])", RegexOptions.Compiled);
|
||||
//look for <date> [h]h:mm[:ss] [PM/AM] [UTC/GMT]
|
||||
m = Regex.Match(str.Substring(parsedDate.IndexOfDate + parsedDate.LengthOfDate), @"(?<=^\s*,?\s+|^\s*at\s*|^\s*[T\-]\s*)(?'hour'\d{1,2})\s*:\s*(?'minute'\d{2})\s*(?::\s*(?'second'\d{2}))?(?:\s*(?'ampm'AM|am|PM|pm))?" + timeZoneR + @"(?=$|[^\d\w])", RegexOptions.Compiled);
|
||||
if (!m.Success)
|
||||
//look for [h]h:mm:ss [PM/AM] [UTC/GMT] <date>
|
||||
m = Regex.Match(str.Substring(0, parsed_date.IndexOfDate), @"(?<=^|[^\d])(?'hour'\d{1,2})\s*:\s*(?'minute'\d{2})\s*(?::\s*(?'second'\d{2}))?(?:\s*(?'ampm'AM|am|PM|pm))?" + time_zone_r + @"(?=$|[\s,]+)", RegexOptions.Compiled);
|
||||
m = Regex.Match(str.Substring(0, parsedDate.IndexOfDate), @"(?<=^|[^\d])(?'hour'\d{1,2})\s*:\s*(?'minute'\d{2})\s*(?::\s*(?'second'\d{2}))?(?:\s*(?'ampm'AM|am|PM|pm))?" + timeZoneR + @"(?=$|[\s,]+)", RegexOptions.Compiled);
|
||||
if (!m.Success)
|
||||
//look for [h]h:mm:ss [PM/AM] [UTC/GMT] within <date>
|
||||
m = Regex.Match(str.Substring(parsed_date.IndexOfDate, parsed_date.LengthOfDate), @"(?<=^|[^\d])(?'hour'\d{1,2})\s*:\s*(?'minute'\d{2})\s*(?::\s*(?'second'\d{2}))?(?:\s*(?'ampm'AM|am|PM|pm))?" + time_zone_r + @"(?=$|[\s,]+)", RegexOptions.Compiled);
|
||||
m = Regex.Match(str.Substring(parsedDate.IndexOfDate, parsedDate.LengthOfDate), @"(?<=^|[^\d])(?'hour'\d{1,2})\s*:\s*(?'minute'\d{2})\s*(?::\s*(?'second'\d{2}))?(?:\s*(?'ampm'AM|am|PM|pm))?" + timeZoneR + @"(?=$|[\s,]+)", RegexOptions.Compiled);
|
||||
}
|
||||
else//look anywhere within string
|
||||
{
|
||||
//look for hh:mm:ss <UTC offset>
|
||||
//look for hh:mm:ss <UTC offset>
|
||||
m = Regex.Match(str, @"(?<=^|\s+|\s*T\s*)(?'hour'\d{2})\s*:\s*(?'minute'\d{2})\s*:\s*(?'second'\d{2})\s+(?'offset_sign'[\+\-])(?'offset_hh'\d{2}):?(?'offset_mm'\d{2})?(?=$|[^\d\w])", RegexOptions.Compiled);
|
||||
if (!m.Success)
|
||||
//look for [h]h:mm[:ss] [PM/AM] [UTC/GMT]
|
||||
m = Regex.Match(str, @"(?<=^|\s+|\s*T\s*)(?'hour'\d{1,2})\s*:\s*(?'minute'\d{2})\s*(?::\s*(?'second'\d{2}))?(?:\s*(?'ampm'AM|am|PM|pm))?" + time_zone_r + @"(?=$|[^\d\w])", RegexOptions.Compiled);
|
||||
m = Regex.Match(str, @"(?<=^|\s+|\s*T\s*)(?'hour'\d{1,2})\s*:\s*(?'minute'\d{2})\s*(?::\s*(?'second'\d{2}))?(?:\s*(?'ampm'AM|am|PM|pm))?" + timeZoneR + @"(?=$|[^\d\w])", RegexOptions.Compiled);
|
||||
}
|
||||
|
||||
if (!m.Success)
|
||||
@@ -396,49 +392,49 @@ namespace DateTimeRoutines
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.Compare(m.Groups["ampm"].Value, "PM", true) == 0 && hour < 12)
|
||||
if ("PM".Equals(m.Groups["ampm"].Value, StringComparison.OrdinalIgnoreCase) && hour < 12)
|
||||
hour += 12;
|
||||
else if (string.Compare(m.Groups["ampm"].Value, "AM", true) == 0 && hour == 12)
|
||||
else if ("AM".Equals(m.Groups["ampm"].Value, StringComparison.OrdinalIgnoreCase) && hour == 12)
|
||||
hour -= 12;
|
||||
|
||||
var date_time = new DateTime(1, 1, 1, hour, minute, second);
|
||||
var dateTime = new DateTime(1, 1, 1, hour, minute, second);
|
||||
|
||||
if (m.Groups["offset_hh"].Success)
|
||||
{
|
||||
var offset_hh = int.Parse(m.Groups["offset_hh"].Value);
|
||||
var offset_mm = 0;
|
||||
var offsetHh = int.Parse(m.Groups["offset_hh"].Value);
|
||||
var offsetMm = 0;
|
||||
if (m.Groups["offset_mm"].Success)
|
||||
offset_mm = int.Parse(m.Groups["offset_mm"].Value);
|
||||
var utc_offset = new TimeSpan(offset_hh, offset_mm, 0);
|
||||
offsetMm = int.Parse(m.Groups["offset_mm"].Value);
|
||||
var utcOffset = new TimeSpan(offsetHh, offsetMm, 0);
|
||||
if (m.Groups["offset_sign"].Value == "-")
|
||||
utc_offset = -utc_offset;
|
||||
parsed_time = new ParsedDateTime(-1, -1, m.Index, m.Length, date_time, utc_offset);
|
||||
utcOffset = -utcOffset;
|
||||
parsedTime = new ParsedDateTime(-1, -1, m.Index, m.Length, dateTime, utcOffset);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m.Groups["time_zone"].Success)
|
||||
{
|
||||
TimeSpan utc_offset;
|
||||
TimeSpan utcOffset;
|
||||
switch (m.Groups["time_zone"].Value)
|
||||
{
|
||||
case "UTC":
|
||||
case "GMT":
|
||||
utc_offset = new TimeSpan(0, 0, 0);
|
||||
utcOffset = new TimeSpan(0, 0, 0);
|
||||
break;
|
||||
case "CST":
|
||||
utc_offset = new TimeSpan(-6, 0, 0);
|
||||
utcOffset = new TimeSpan(-6, 0, 0);
|
||||
break;
|
||||
case "EST":
|
||||
utc_offset = new TimeSpan(-5, 0, 0);
|
||||
utcOffset = new TimeSpan(-5, 0, 0);
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Time zone: " + m.Groups["time_zone"].Value + " is not defined.");
|
||||
}
|
||||
parsed_time = new ParsedDateTime(-1, -1, m.Index, m.Length, date_time, utc_offset);
|
||||
parsedTime = new ParsedDateTime(-1, -1, m.Index, m.Length, dateTime, utcOffset);
|
||||
return true;
|
||||
}
|
||||
|
||||
parsed_time = new ParsedDateTime(-1, -1, m.Index, m.Length, date_time);
|
||||
parsedTime = new ParsedDateTime(-1, -1, m.Index, m.Length, dateTime);
|
||||
//}
|
||||
//catch(Exception e)
|
||||
//{
|
||||
@@ -448,17 +444,17 @@ namespace DateTimeRoutines
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find date within the passed string and return it as ParsedDateTime object.
|
||||
/// Tries to find date within the passed string and return it as ParsedDateTime object.
|
||||
/// It recognizes only date while ignoring time, so time in the returned ParsedDateTime is always 0:0:0.
|
||||
/// If year of the date was not found then it accepts the current year.
|
||||
/// If year of the date was not found then it accepts the current year.
|
||||
/// </summary>
|
||||
/// <param name="str">string that contains date</param>
|
||||
/// <param name="default_format">format to be used preferably in ambivalent instances</param>
|
||||
/// <param name="parsed_date">parsed date output</param>
|
||||
/// <param name="defaultFormat">format to be used preferably in ambivalent instances</param>
|
||||
/// <param name="parsedDate">parsed date output</param>
|
||||
/// <returns>true if date was found, else false</returns>
|
||||
public static bool TryParseDate(this string str, DateTimeFormat default_format, out ParsedDateTime parsed_date)
|
||||
public static bool TryParseDate(this string str, DateTimeFormat defaultFormat, out ParsedDateTime parsedDate)
|
||||
{
|
||||
parsed_date = null;
|
||||
parsedDate = null;
|
||||
|
||||
if (string.IsNullOrEmpty(str))
|
||||
return false;
|
||||
@@ -468,17 +464,17 @@ namespace DateTimeRoutines
|
||||
if (m.Success)
|
||||
{
|
||||
DateTime date;
|
||||
if ((default_format ^ DateTimeFormat.USA_DATE) == DateTimeFormat.USA_DATE)
|
||||
if ((defaultFormat ^ DateTimeFormat.UsaDate) == DateTimeFormat.UsaDate)
|
||||
{
|
||||
if (!convert_to_date(int.Parse(m.Groups["year"].Value), int.Parse(m.Groups["day"].Value), int.Parse(m.Groups["month"].Value), out date))
|
||||
if (!ConvertToDate(int.Parse(m.Groups["year"].Value), int.Parse(m.Groups["day"].Value), int.Parse(m.Groups["month"].Value), out date))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!convert_to_date(int.Parse(m.Groups["year"].Value), int.Parse(m.Groups["month"].Value), int.Parse(m.Groups["day"].Value), out date))
|
||||
if (!ConvertToDate(int.Parse(m.Groups["year"].Value), int.Parse(m.Groups["month"].Value), int.Parse(m.Groups["day"].Value), out date))
|
||||
return false;
|
||||
}
|
||||
parsed_date = new ParsedDateTime(m.Index, m.Length, -1, -1, date);
|
||||
parsedDate = new ParsedDateTime(m.Index, m.Length, -1, -1, date);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -486,9 +482,9 @@ namespace DateTimeRoutines
|
||||
m = Regex.Match(str, @"(?<=^|[^\d])(?'year'\d{2}|\d{4})\s*(?'separator'[\-])\s*(?'month'\d{1,2})\s*\'separator'+\s*(?'day'\d{1,2})(?=$|[^\d])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
if (m.Success)
|
||||
{
|
||||
if (!convert_to_date(int.Parse(m.Groups["year"].Value), int.Parse(m.Groups["month"].Value), int.Parse(m.Groups["day"].Value), out var date))
|
||||
if (!ConvertToDate(int.Parse(m.Groups["year"].Value), int.Parse(m.Groups["month"].Value), int.Parse(m.Groups["day"].Value), out var date))
|
||||
return false;
|
||||
parsed_date = new ParsedDateTime(m.Index, m.Length, -1, -1, date);
|
||||
parsedDate = new ParsedDateTime(m.Index, m.Length, -1, -1, date);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -509,8 +505,8 @@ namespace DateTimeRoutines
|
||||
if (m.Success)
|
||||
{
|
||||
var month = -1;
|
||||
var index_of_date = m.Index;
|
||||
var length_of_date = m.Length;
|
||||
var indexOfDate = m.Index;
|
||||
var lengthOfDate = m.Length;
|
||||
|
||||
switch (m.Groups["month"].Value)
|
||||
{
|
||||
@@ -563,22 +559,19 @@ namespace DateTimeRoutines
|
||||
break;
|
||||
}
|
||||
|
||||
int year;
|
||||
if (!string.IsNullOrEmpty(m.Groups["year"].Value))
|
||||
year = int.Parse(m.Groups["year"].Value);
|
||||
else
|
||||
year = DefaultDate.Year;
|
||||
var year = !string.IsNullOrEmpty(m.Groups["year"].Value) ?
|
||||
int.Parse(m.Groups["year"].Value) : DefaultDate.Year;
|
||||
|
||||
if (!convert_to_date(year, month, int.Parse(m.Groups["day"].Value), out var date))
|
||||
if (!ConvertToDate(year, month, int.Parse(m.Groups["day"].Value), out var date))
|
||||
return false;
|
||||
parsed_date = new ParsedDateTime(index_of_date, length_of_date, -1, -1, date);
|
||||
parsedDate = new ParsedDateTime(indexOfDate, lengthOfDate, -1, -1, date);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool convert_to_date(int year, int month, int day, out DateTime date)
|
||||
private static bool ConvertToDate(int year, int month, int day, out DateTime date)
|
||||
{
|
||||
if (year >= 100)
|
||||
{
|
||||
|
@@ -182,6 +182,7 @@ function displayConfiguredIndexersList(indexers) {
|
||||
indexersTable.find("table").dataTable(
|
||||
{
|
||||
"stateSave": true,
|
||||
"stateDuration": 0,
|
||||
"pageLength": -1,
|
||||
"lengthMenu": [[10, 20, 50, 100, 250, 500, -1], [10, 20, 50, 100, 250, 500, "All"]],
|
||||
"order": [[0, "asc"]],
|
||||
@@ -253,6 +254,7 @@ function displayUnconfiguredIndexersList() {
|
||||
indexersTable.find("table").DataTable(
|
||||
{
|
||||
"stateSave": true,
|
||||
"stateDuration": 0,
|
||||
"fnStateSaveParams": function (oSettings, sValue) {
|
||||
sValue.search.search = ""; // don't save the search filter content
|
||||
return sValue;
|
||||
@@ -955,6 +957,7 @@ function updateSearchResultTable(element, results) {
|
||||
|
||||
"dom": "lfr<\"dataTables_deadfilter\">tip",
|
||||
"stateSave": true,
|
||||
"stateDuration": 0,
|
||||
"bAutoWidth": false,
|
||||
"pageLength": 20,
|
||||
"lengthMenu": [[10, 20, 50, 100, 250, 500, -1], [10, 20, 50, 100, 250, 500, "All"]],
|
||||
@@ -1082,6 +1085,7 @@ function bindUIButtons() {
|
||||
table.DataTable(
|
||||
{
|
||||
"stateSave": true,
|
||||
"stateDuration": 0,
|
||||
"bAutoWidth": false,
|
||||
"pageLength": 20,
|
||||
"lengthMenu": [[10, 20, 50, -1], [10, 20, 50, "All"]],
|
||||
|
@@ -691,6 +691,6 @@
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="../libs/api.js?changed=2017083001"></script>
|
||||
<script type="text/javascript" src="../custom.js?changed=20200301"></script>
|
||||
<script type="text/javascript" src="../custom.js?changed=20200316"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -158,8 +158,10 @@
|
||||
keywordsfilters:
|
||||
- name: re_replace
|
||||
args: ["([12][0-9]{3})", ""]
|
||||
|
||||
rows:
|
||||
selector: div#fancy-list-group ul.list-group li.list-group-item
|
||||
|
||||
fields:
|
||||
_quality:
|
||||
selector: div.list-group-item-content p.m-0 span.badge-primary:contains("1080p"), div.list-group-item-content p.m-0 span.badge-primary:contains("720p"), div.list-group-item-content p.m-0 span.badge-primary:contains("4k")
|
||||
@@ -196,69 +198,70 @@
|
||||
attribute: src
|
||||
case:
|
||||
# unfortunately the site does not have category numbers on the results page, just a .png which can apply to both movies and tv.
|
||||
# therefore I've order the list to favour movies,
|
||||
# therefore I've order the list to favour movies,
|
||||
# also, not all .png have been verified as the site has many empty categories. their image names have been extrapolated from the cat desc.
|
||||
"[src$=\"/XXXZ.png\"]": "74"
|
||||
"[src$=\"/Hentai.png\"]": "75"
|
||||
"[src$=\"/Anime.png\"]": "69"
|
||||
"[src$=\"/Windows.png\"]": "21"
|
||||
"[src$=\"/Mac.png\"]": "22"
|
||||
"[src$=\"/Android.png\"]": "23"
|
||||
"[src$=\"/Linux.png\"]": "25"
|
||||
"[src$=\"/iPhone.png\"]": "26"
|
||||
"[src$=\"/iPad.png\"]": "27"
|
||||
"[src$=\"/iPod.png\"]": "28"
|
||||
"[src$=\"/Cursos.png\"]": "71"
|
||||
"[src$=\"/Apostila.png\"]": "72"
|
||||
"[src$=\"/E-books.png\"]": "67"
|
||||
"[src$=\"/Revista.png\"]": "68"
|
||||
"[src$=\"/HQ.png\"]": "112"
|
||||
"[src$=\"/Filmes.png\"]": "119"
|
||||
"[src$=\"/Revistas-XXX.png\"]": "70"
|
||||
"[src$=\"/PS4.png\"]": "79"
|
||||
"[src$=\"/Jogos-PC.png\"]": "47"
|
||||
"[src$=\"/Emulador.png\"]": "61"
|
||||
"[src$=\"/Emulacao.png\"]": "109"
|
||||
"[src$=\"/Jogos-Mac.png\"]": "48"
|
||||
"[src$=\"/Jogos-PS1.png\"]": "49"
|
||||
"[src$=\"/Jogos-PS2.png\"]": "50"
|
||||
"[src$=\"/Jogos-PS3.png\"]": "51"
|
||||
"[src$=\"/Jogos-Dreamcast.png\"]": "52"
|
||||
"[src$=\"/Jogos-Xbox360.png\"]": "54"
|
||||
"[src$=\"/Jogos-Xbox.png\"]": "56"
|
||||
"[src$=\"/Jogos-Wii.png\"]": "55"
|
||||
"[src$=\"/Jogos-DS.png\"]": "58"
|
||||
"[src$=\"/jogosandroid.png\"]": "57"
|
||||
"[src$=\"/Jogos-PSP.png\"]": "82"
|
||||
"[src$=\"/Jogos-NS.png\"]": "110"
|
||||
"[src$=\"/Jogos-XboxOne.png\"]": "78"
|
||||
"[src$=\"/Axe.png\"]": "29"
|
||||
"[src$=\"/Funk.png\"]": "31"
|
||||
"[src$=\"/Pagode.png\"]": "32"
|
||||
"[src$=\"/HIP_HOP.png\"]": "33"
|
||||
"[src$=\"/Rap.png\"]": "34"
|
||||
"[src$=\"/Rock.png\"]": "36"
|
||||
"[src$=\"/Hard-Rock.png\"]": "37"
|
||||
"[src$=\"/Blues.png\"]": "38"
|
||||
"[src$=\"/Dance.png\"]": "39"
|
||||
"[src$=\"/Discografia.png\"]": "40"
|
||||
"[src$=\"/Erudita.png\"]": "41"
|
||||
"[src$=\"/Forro.png\"]": "42"
|
||||
"[src$=\"/Dubstep.png\"]": "43"
|
||||
"[src$=\"/Sertanejo.png\"]": "46"
|
||||
"[src$=\"/Samba.png\"]": "45"
|
||||
"[src$=\"/Musica-Outros.png\"]": "77"
|
||||
"[src$=\"/Reggae.png\"]": "76"
|
||||
"[src$=\"/Gospel.png\"]": "83"
|
||||
"[src$=\"/POP.png\"]": "115"
|
||||
"[src$=\"/MPB.png\"]": "114"
|
||||
"[src$=\"/OST.png\"]": "84"
|
||||
"[src$=\"/Seriados.png\"]": "120"
|
||||
"[src$=\"/Shows.png\"]": "65"
|
||||
"[src$=\"/Aberta.png\"]": "63"
|
||||
"[src$=\"/Esporte.png\"]": "62"
|
||||
"[src$=\"/Fechada.png\"]": "64"
|
||||
"[src$=\"/Videoaula.png\"]": "73"
|
||||
"[src$=\"/XXXZ.png\"]": 74
|
||||
"[src$=\"/Hentai.png\"]": 75
|
||||
"[src$=\"/Anime.png\"]": 69
|
||||
"[src$=\"/Windows.png\"]": 21
|
||||
"[src$=\"/Mac.png\"]": 22
|
||||
"[src$=\"/Android.png\"]": 23
|
||||
"[src$=\"/Linux.png\"]": 25
|
||||
"[src$=\"/iPhone.png\"]": 26
|
||||
"[src$=\"/iPad.png\"]": 27
|
||||
"[src$=\"/iPod.png\"]": 28
|
||||
"[src$=\"/Cursos.png\"]": 71
|
||||
"[src$=\"/Apostila.png\"]": 72
|
||||
"[src$=\"/E-books.png\"]": 67
|
||||
"[src$=\"/Revista.png\"]": 68
|
||||
"[src$=\"/HQ.png\"]": 112
|
||||
"[src$=\"/Filmes.png\"]": 119
|
||||
"[src$=\"/Revistas-XXX.png\"]": 70
|
||||
"[src$=\"/PS4.png\"]": 79
|
||||
"[src$=\"/Jogos-PC.png\"]": 47
|
||||
"[src$=\"/Emulador.png\"]": 61
|
||||
"[src$=\"/Emulacao.png\"]": 109
|
||||
"[src$=\"/Jogos-Mac.png\"]": 48
|
||||
"[src$=\"/Jogos-PS1.png\"]": 49
|
||||
"[src$=\"/Jogos-PS2.png\"]": 50
|
||||
"[src$=\"/Jogos-PS3.png\"]": 51
|
||||
"[src$=\"/Jogos-Dreamcast.png\"]": 52
|
||||
"[src$=\"/Jogos-Xbox360.png\"]": 54
|
||||
"[src$=\"/Jogos-Xbox.png\"]": 56
|
||||
"[src$=\"/Jogos-Wii.png\"]": 55
|
||||
"[src$=\"/Jogos-DS.png\"]": 58
|
||||
"[src$=\"/jogosandroid.png\"]": 57
|
||||
"[src$=\"/Jogos-PSP.png\"]": 82
|
||||
"[src$=\"/Jogos-NS.png\"]": 110
|
||||
"[src$=\"/Jogos-XboxOne.png\"]": 78
|
||||
"[src$=\"/Axe.png\"]": 29
|
||||
"[src$=\"/Funk.png\"]": 31
|
||||
"[src$=\"/Pagode.png\"]": 32
|
||||
"[src$=\"/HIP_HOP.png\"]": 33
|
||||
"[src$=\"/Rap.png\"]": 34
|
||||
"[src$=\"/Rock.png\"]": 36
|
||||
"[src$=\"/Hard-Rock.png\"]": 37
|
||||
"[src$=\"/Blues.png\"]": 38
|
||||
"[src$=\"/Dance.png\"]": 39
|
||||
"[src$=\"/Discografia.png\"]": 40
|
||||
"[src$=\"/Erudita.png\"]": 41
|
||||
"[src$=\"/Forro.png\"]": 42
|
||||
"[src$=\"/Dubstep.png\"]": 43
|
||||
"[src$=\"/Sertanejo.png\"]": 46
|
||||
"[src$=\"/Samba.png\"]": 45
|
||||
"[src$=\"/Musica-Outros.png\"]": 77
|
||||
"[src$=\"/Reggae.png\"]": 76
|
||||
"[src$=\"/Gospel.png\"]": 83
|
||||
"[src$=\"/POP.png\"]": 115
|
||||
"[src$=\"/MPB.png\"]": 114
|
||||
"[src$=\"/OST.png\"]": 84
|
||||
"[src$=\"/Seriados.png\"]": 120
|
||||
"[src$=\"/Shows.png\"]": 65
|
||||
"[src$=\"/Aberta.png\"]": 63
|
||||
"[src$=\"/Esporte.png\"]": 62
|
||||
"[src$=\"/Fechada.png\"]": 64
|
||||
"[src$=\"/Videoaula.png\"]": 73
|
||||
"*": 999
|
||||
date:
|
||||
text: now
|
||||
date:
|
||||
@@ -284,5 +287,5 @@
|
||||
"span.badge-success:contains(\"FREE\")": 0
|
||||
"*": 1
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": 1
|
||||
text: 1
|
||||
# engine n/a
|
||||
|
@@ -1,9 +1,9 @@
|
||||
---
|
||||
site: baibako
|
||||
name: BaibaKo
|
||||
description: "BaibaKo is a RUSSIAN Semi-Private Torrent Tracker for TV"
|
||||
description: "BaibaKo is a RUSSIAN Private Torrent Tracker for TV"
|
||||
language: ru-ru
|
||||
type: semi-private
|
||||
type: private
|
||||
encoding: windows-1251
|
||||
links:
|
||||
- http://baibako.tv/ # site does not support https NET::ERR_CERT_AUTHORITY_INVALID
|
||||
|
@@ -20,6 +20,7 @@
|
||||
- {id: 67, cat: XXX, desc: "XxX"}
|
||||
- {id: 68, cat: Movies, desc: "Dual (TR-~)"}
|
||||
- {id: 58, cat: Movies, desc: "Film"}
|
||||
- {id: 72, cat: Movies, desc: "Belgesel"}
|
||||
- {id: 60, cat: Movies/3D, desc: "3D"}
|
||||
- {id: 45, cat: Movies, desc: "Eğitim / Prog "}
|
||||
- {id: 1, cat: Movies, desc: "Filmler"}
|
||||
|
@@ -13,6 +13,8 @@
|
||||
- {id: 1, cat: Movies, desc: "Movies"}
|
||||
- {id: 2, cat: TV, desc: "TV"}
|
||||
- {id: 3, cat: Movies, desc: "FANRES"}
|
||||
- {id: 6, cat: TV/Anime, desc: "Anime"}
|
||||
- {id: 7, cat: Movies/Other, desc: "Trailer"}
|
||||
|
||||
modes:
|
||||
search: [q, imdbid]
|
||||
|
@@ -59,6 +59,7 @@
|
||||
search: [q, imdbid]
|
||||
tv-search: [q, season, ep, imdbid]
|
||||
movie-search: [q, imdbid]
|
||||
music-search: [q]
|
||||
|
||||
login:
|
||||
path: index.php?page=login
|
||||
@@ -81,8 +82,9 @@
|
||||
page: torrents
|
||||
search: "{{ if .Query.IMDBID }}{{ .Query.IMDBID }}{{else}}{{ .Keywords }}{{end}}"
|
||||
category: "{{ if .Categories }}{{ range .Categories }}{{.}};{{end}}{{else}}0{{end}}"
|
||||
# options: 0=title, 1=title&descr, 2=descr
|
||||
options: "{{ if .Query.IMDBID }}1{{else}}0{{end}}"
|
||||
# 0=title, 1=title&descr, 2=descr
|
||||
options: "{{ if .Query.IMDBID }}2{{else}}0{{end}}"
|
||||
# 0 all 1 activeonly 2 deadonly
|
||||
active: 0
|
||||
order: "{{ .Config.sort }}"
|
||||
by: "{{ .Config.type }}"
|
||||
|
@@ -31,9 +31,9 @@
|
||||
- {id: 21, cat: Books, desc: "Book"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
search: [q, imdbid]
|
||||
tv-search: [q, season, ep, imdbid]
|
||||
movie-search: [q, imdbid]
|
||||
|
||||
settings:
|
||||
- name: cookie
|
||||
@@ -67,17 +67,19 @@
|
||||
test:
|
||||
path: index.php
|
||||
selector: a[href^="/logout.php"]
|
||||
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: browse.php
|
||||
inputs:
|
||||
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
search: "{{ if .Query.IMDBID }}{{ .Query.IMDBID }}{{else}}{{ .Keywords }}{{end}}"
|
||||
# 0 onlyactive 1 all 4 requests 5 onlydead 6 polish 8 free 10 doubleup 11 premier 13 VOD
|
||||
incldead: 1
|
||||
# 0 title 1 descr
|
||||
blah: 0
|
||||
blah: "{{ if .Query.IMDBID }}1{{else}}0{{end}}"
|
||||
gatunek: 0
|
||||
quality: none
|
||||
sort: "{{ .Config.sort }}"
|
||||
type: "{{ .Config.type }}"
|
||||
|
||||
@@ -98,13 +100,17 @@
|
||||
attribute: href
|
||||
download:
|
||||
selector: a[href^="download.php/"]
|
||||
attribute: href
|
||||
attribute: href
|
||||
banner:
|
||||
selector: a[href^="details.php?id="]
|
||||
attribute: onmouseover
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "src=(.+?) "
|
||||
imdb:
|
||||
optional: true
|
||||
selector: a[href*="www.imdb.com/title/tt"]
|
||||
attribute: href
|
||||
description:
|
||||
optional: true
|
||||
selector: img[src="pic/Poland.png"]
|
||||
|
@@ -12,20 +12,21 @@
|
||||
categorymappings:
|
||||
# Documentaire
|
||||
- {id: 96, cat: TV/Documentary, desc: "Docus TV"}
|
||||
- {id: 124, cat: TV/Documentary, desc: "Docus Formation Video"}
|
||||
# Films
|
||||
- {id: 49, cat: Movies/3D, desc: "Films 3D"}
|
||||
- {id: 1, cat: XXX, desc: "3X"}
|
||||
- {id: 48, cat: XXX, desc: "3X HD1080p"}
|
||||
- {id: 47, cat: XXX, desc: "3X HD720p"}
|
||||
- {id: 50, cat: Movies/UHD, desc: "Films 4K"}
|
||||
- {id: 51, cat: Movies/UHD, desc: "Films 4K UHDTV"}
|
||||
- {id: 51, cat: TV/UHD, desc: "Films 4K UHDTV"}
|
||||
- {id: 57, cat: Movies/BluRay, desc: "Films BDRip"}
|
||||
- {id: 56, cat: Movies/BluRay, desc: "Films Blu-Ray"}
|
||||
- {id: 58, cat: Movies/BluRay, desc: "Films BRRip"}
|
||||
- {id: 66, cat: Movies, desc: "Films CamTS"}
|
||||
- {id: 59, cat: Movies/DVD, desc: "Films DVDR"}
|
||||
- {id: 60, cat: Movies/DVD, desc: "Films DVDRip"}
|
||||
- {id: 98, cat: Movies, desc: "Films Film Animées"}
|
||||
- {id: 98, cat: TV/Anime, desc: "Films Film Animées"}
|
||||
- {id: 65, cat: Movies, desc: "Films FSCR"}
|
||||
- {id: 52, cat: Movies/HD, desc: "Films HD 720p"}
|
||||
- {id: 53, cat: Movies/HD, desc: "Films HD1080p"}
|
||||
@@ -124,7 +125,7 @@
|
||||
options:
|
||||
"desc": "desc"
|
||||
"asc": "asc"
|
||||
|
||||
|
||||
login:
|
||||
method: cookie
|
||||
inputs:
|
||||
@@ -149,13 +150,17 @@
|
||||
# http://crazyspirits.com/torrents-search.php?c101=1&c102=1&c103=1&c104=1&c105=1&c106=1&search=&cat=0&incldead=0&freeleech=0&lang=0
|
||||
- path: torrents-search.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
# 0 active 1 incldead 2 onlydead
|
||||
incldead: 1
|
||||
# 0 all 1 inclfree 2 onlyfree
|
||||
freeleech: 0
|
||||
# 0 all 1 french 2 english etc
|
||||
lang: 0
|
||||
sort: "{{ .Config.sort }}"
|
||||
order: "{{ .Config.type }}"
|
||||
# imdb search not supported and imdb links not in results.
|
||||
|
||||
rows:
|
||||
selector: table.border_table > tbody > tr.t-row
|
||||
@@ -192,13 +197,13 @@
|
||||
attribute: onmouseover
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "Taille : </b>(.*?)<br"
|
||||
args: "Taille : </b>(.*?)<br"
|
||||
date:
|
||||
selector: a[onmouseover][href^="torrents-details.php?id="]
|
||||
attribute: onmouseover
|
||||
filters:
|
||||
- name: regexp
|
||||
args: " Le : </b>(.*?)<br"
|
||||
args: " Le : </b>(.*?)<br"
|
||||
- name: dateparse
|
||||
args: "02-01-2006"
|
||||
downloadvolumefactor:
|
||||
@@ -206,6 +211,5 @@
|
||||
img[src="images/Torrents/free.png"]: 0
|
||||
"*": 1
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": 1
|
||||
text: 1
|
||||
# TT3.0
|
||||
|
@@ -100,15 +100,17 @@
|
||||
search:
|
||||
paths:
|
||||
# http://www.crnaberza.com/browse.php?c52=1&c20=1&c34=1&incldead=1&search=star+trek
|
||||
- path: browse.php
|
||||
- path: browse.php
|
||||
inputs:
|
||||
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
searchin: title
|
||||
# 0 title 1 descr 2 both
|
||||
blah: 0
|
||||
# 0 active 1 incldead 2 onlydead
|
||||
incldead: 1
|
||||
sort: "{{ .Config.sort }}"
|
||||
type: "{{ .Config.type }}"
|
||||
# does not support searching imdbid and does not return imdb links
|
||||
|
||||
rows:
|
||||
selector: tr:has(td.trowtorrent)
|
||||
|
@@ -22,7 +22,7 @@
|
||||
- {id: 38, cat: Movies/HD, desc: "HD-LQ"}
|
||||
- {id: 3, cat: PC/Games, desc: "Hry"}
|
||||
- {id: 2, cat: Audio, desc: "Hudba"}
|
||||
- {id: 34, cat: Audio/Video, desc: "Hudba DVD/HD"}
|
||||
- {id: 34, cat: Audio/Video, desc: "Hudební video"}
|
||||
- {id: 6, cat: Books, desc: "Knihy"}
|
||||
- {id: 13, cat: Console, desc: "Konzole"}
|
||||
- {id: 32, cat: Audio, desc: "Mluvené slovo"}
|
||||
@@ -30,7 +30,6 @@
|
||||
- {id: 4, cat: Other, desc: "Ostatní"}
|
||||
- {id: 25, cat: TV, desc: "Seriály"}
|
||||
- {id: 29, cat: Audio, desc: "Soundtrack"}
|
||||
- {id: 19, cat: Audio/Video, desc: "Videoklipy"}
|
||||
- {id: 24, cat: XXX, desc: "xXx"}
|
||||
|
||||
modes:
|
||||
@@ -74,12 +73,14 @@
|
||||
inputs:
|
||||
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
|
||||
s: "{{ .Keywords }}"
|
||||
# 0 approved 1 all 2 not approved
|
||||
t: 1
|
||||
o: "{{ .Config.sort }}"
|
||||
# does not support imdbid search and does not return imdb links
|
||||
|
||||
rows:
|
||||
selector: tr.torr_hover
|
||||
|
||||
|
||||
keywordsfilters:
|
||||
- name: re_replace
|
||||
args: ["S[0-9]{2}([^E]|$)", ""] # remove season tag without episode (search doesn't support it)
|
||||
@@ -93,17 +94,17 @@
|
||||
- name: diacritics
|
||||
args: replace
|
||||
- name: replace
|
||||
args: ["1080i", "1080p"]
|
||||
args: ["1080i", "1080p"]
|
||||
- name: replace
|
||||
args: ["720i", "720p"]
|
||||
args: ["720i", "720p"]
|
||||
- name: replace
|
||||
args: ["pLQ", "p"]
|
||||
args: ["pLQ", "p"]
|
||||
- name: replace
|
||||
args: ["pHD", "p"]
|
||||
args: ["pHD", "p"]
|
||||
- name: replace
|
||||
args: ["serie", ""]
|
||||
args: ["serie", ""]
|
||||
- name: replace
|
||||
args: ["Serie", ""]
|
||||
args: ["Serie", ""]
|
||||
- name: re_replace
|
||||
args: ["(\\d{2})\\.", "S$1"]
|
||||
- name: re_replace
|
||||
@@ -152,7 +153,7 @@
|
||||
args: [ "|", 2 ]
|
||||
- name: append
|
||||
args: " +02:00"
|
||||
- name: dateparse
|
||||
- name: dateparse
|
||||
args: "2.1.2006 15:04 -07:00"
|
||||
seeders:
|
||||
selector: td:nth-child(7) span
|
||||
|
@@ -54,9 +54,33 @@
|
||||
- {id: 12, cat: XXX, desc: "XxX"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
movie-search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
search: [q, imdbid]
|
||||
tv-search: [q, season, ep, imdbid]
|
||||
movie-search: [q, imdbid]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
- name: sort
|
||||
type: select
|
||||
label: Sort requested from site
|
||||
default: "4"
|
||||
options:
|
||||
"4": "created"
|
||||
"7": "seeders"
|
||||
"5": "size"
|
||||
"1": "title"
|
||||
- name: type
|
||||
type: select
|
||||
label: Order requested from site
|
||||
default: "desc"
|
||||
options:
|
||||
"desc": "desc"
|
||||
"asc": "asc"
|
||||
|
||||
login:
|
||||
path: takelogin.php
|
||||
@@ -68,7 +92,7 @@
|
||||
- selector: td.embedded:has(h2:contains("failed"))
|
||||
test:
|
||||
path: browse.php
|
||||
|
||||
|
||||
ratio:
|
||||
path: browse.php
|
||||
selector: font:contains("Ratio:") > span
|
||||
@@ -77,9 +101,14 @@
|
||||
paths:
|
||||
- path: browse.php
|
||||
inputs:
|
||||
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
incldead: 1
|
||||
$raw: "{{ if .Categories}}{{ range .Categories }}c{{.}}=1&{{end}}{{else}}cat=0{{end}}"
|
||||
search: "{{ if .Query.IMDBID }}{{ .Query.IMDBID }}{{else}}{{ .Keywords }}{{end}}"
|
||||
# 1 active 2 incldead 3 onlydead 4 free
|
||||
incldead: 2
|
||||
# 0 name 1 genre 2 descr 3 imdb
|
||||
blah: "{{ if .Query.IMDBID }}3{{else}}0{{end}}"
|
||||
sort: "{{ .Config.sort }}"
|
||||
type: "{{ .Config.type }}"
|
||||
|
||||
rows:
|
||||
selector: div.ncls > table > tbody > tr:has(a.tname)
|
||||
|
@@ -26,9 +26,9 @@
|
||||
- {id: 8, cat: TV, desc: "TV shows"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
search: [q, imdbid]
|
||||
tv-search: [q, season, ep, imdbid]
|
||||
movie-search: [q, imdbid]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
@@ -69,10 +69,14 @@
|
||||
- path: browse.php
|
||||
inputs:
|
||||
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
search: "{{ if .Query.IMDBID }}{{ .Query.IMDBID }}{{else}}{{ .Keywords }}{{end}}"
|
||||
# active 1 incldead 2 onlydead
|
||||
incldead: 1
|
||||
# 1 = look in description
|
||||
Lysing: "{{ if .Query.IMDBID }}1{{else}}{{end}}"
|
||||
sort: "{{ .Config.sort }}"
|
||||
type: "{{ .Config.type }}"
|
||||
# does not return imdb links
|
||||
|
||||
rows:
|
||||
selector: table[class="torrentlist"] > tbody > tr:has(a[href*="details.php?id="])
|
||||
|
@@ -12,7 +12,7 @@
|
||||
categorymappings:
|
||||
- {id: 60, cat: Movies/HD, desc: "MicroHD 720p"}
|
||||
- {id: 77, cat: Movies/HD, desc: "MicroHD 1080p"}
|
||||
- {id: 78, cat: Movies/HD, desc: "MicroHD 4k"}
|
||||
- {id: 78, cat: Movies/HD, desc: "MicroHD 4K"}
|
||||
- {id: 64, cat: TV/HD, desc: "Pack Series"}
|
||||
- {id: 65, cat: TV/HD, desc: "Pack Series VOSE"}
|
||||
- {id: 80, cat: Movies/BluRay, desc: "BDRip X265 1080p"}
|
||||
@@ -43,13 +43,13 @@
|
||||
- {id: 48, cat: Movies/BluRay, desc: "BR-Rip/HD-Rip"}
|
||||
- {id: 17, cat: Movies/BluRay, desc: "BD-Rip"}
|
||||
- {id: 5, cat: TV/Anime, desc: "Anime"}
|
||||
- {id: 34, cat: PC/Games, desc: "Juegos Pc"}
|
||||
- {id: 7, cat: PC/0day, desc: "Software Pc"}
|
||||
- {id: 34, cat: PC/Games, desc: "Juegos PC"}
|
||||
- {id: 7, cat: PC/0day, desc: "Software PC"}
|
||||
- {id: 35, cat: Console, desc: "Juegos Sony"}
|
||||
- {id: 36, cat: PC/Games, desc: "Juegos Microsoft"}
|
||||
- {id: 37, cat: Console/NDS, desc: "Juegos Nintendo"}
|
||||
- {id: 45, cat: Audio/MP3, desc: "Music MP3"}
|
||||
- {id: 44, cat: Audio/Lossless, desc: "Music flac"}
|
||||
- {id: 44, cat: Audio/Lossless, desc: "Music FLAC"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
@@ -84,7 +84,7 @@
|
||||
type: info
|
||||
label: Results Per Page
|
||||
default: For best results, change the <b>Torrents per page:</b> setting to <b>100</b> on your account profile.
|
||||
|
||||
|
||||
login:
|
||||
method: cookie
|
||||
inputs:
|
||||
@@ -99,11 +99,14 @@
|
||||
inputs:
|
||||
page: "torrents"
|
||||
category: "{{ range .Categories }}{{.}};{{end}}"
|
||||
search: "{{ if .Keywords }}{{ .Keywords }}{{else}}{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
# 0 all 1 activeonly 2 deadonly
|
||||
active: 0
|
||||
# 0 title 1 title&desc 2 descr 3 uploaders 5 gold 6 silver 9 2x 10 3x 11 4x
|
||||
options: 0
|
||||
order: "{{ .Config.sort }}"
|
||||
by: "{{ .Config.type }}"
|
||||
# does not support imdbid search and does not supply imdb link in results.
|
||||
|
||||
rows:
|
||||
selector: table.table.table-bordered > tbody > tr:has(a[href^="download.php?id="])
|
||||
|
@@ -74,7 +74,8 @@
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
tv-search: [q]
|
||||
movie-search: [q]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
@@ -125,11 +126,15 @@
|
||||
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
cat: 0
|
||||
# 0 active 1 incldead 2 onlydead
|
||||
incldead: 1
|
||||
# 0 all 1 notfree 2 onlyfree
|
||||
freeleech: 0
|
||||
# 0 all 1 english 2 french etc
|
||||
lang: 0
|
||||
sort: "{{ .Config.sort }}"
|
||||
order: "{{ .Config.type }}"
|
||||
# does not support imdbid search and does not return imdb link in results
|
||||
|
||||
rows:
|
||||
selector: table.ttable_headinner tr.t-row
|
||||
|
@@ -198,11 +198,13 @@
|
||||
page: torrents
|
||||
search: "{{ .Keywords }}"
|
||||
category: "{{ range .Categories }}{{.}};{{end}}"
|
||||
# 0 title 1 title&descr 2 descr
|
||||
options: 0
|
||||
active: "{{ .Config.active }}"
|
||||
gold: "{{ .Config.gold }}"
|
||||
order: "{{ .Config.sort }}"
|
||||
by: "{{ .Config.type }}"
|
||||
# does not support imdbid search and does not ruturn imdb link in results
|
||||
|
||||
rows:
|
||||
selector: table > tbody > tr > td > table.lista > tbody > tr:has(td[onmouseover="this.className='post'"])
|
||||
|
@@ -81,11 +81,16 @@
|
||||
|
||||
search:
|
||||
paths:
|
||||
# http://estone.cc/letoltes.php?kereses_nev=wide&miben=0&cimke=&cat=0&submit.x=40&submit.y=9
|
||||
- path: letoltes.php
|
||||
method: get
|
||||
inputs:
|
||||
$raw: "{{ range .Categories }}kat[]={{.}}&{{end}}{{ .Config.sort }}={{ .Config.type }}"
|
||||
kereses_nev: "{{ .Keywords }}"
|
||||
# 0 name 1 performer 2 director
|
||||
miben: 0
|
||||
# genre
|
||||
cimke: ""
|
||||
# does not support imdbid search and does not return imdb link in results
|
||||
|
||||
rows:
|
||||
selector: body > div[id^="torrent_"]
|
||||
|
@@ -77,6 +77,14 @@
|
||||
# options:
|
||||
# "DESC": "desc"
|
||||
# "ASC": "asc"
|
||||
- name: browseadult
|
||||
type: checkbox
|
||||
label: Use the BrowseAdult search engine
|
||||
default: false
|
||||
- name: info_browseadult
|
||||
type: info
|
||||
label: "About the BrowseAdult search engine"
|
||||
default: "The regular <b>Browse</b> search engine does not return <i>Adult category</i> results.</br>The <b>BrowseAdult</b> search engine can return <i>all category</i> results, but without the <i>imdb tags</i>, and also does not support <i>imdbid</i> searches."
|
||||
|
||||
login:
|
||||
path: login
|
||||
@@ -97,15 +105,13 @@
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: browse.php
|
||||
categorymappings: ["!", 9, 11, 58]
|
||||
- path: browseadult.php
|
||||
categorymappings: [9, 11, 58]
|
||||
- path: "{{ if .Config.browseadult }}browseadult.php{{else}}browse.php{{end}}"
|
||||
inputs:
|
||||
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
|
||||
search: "{{ if .Query.IMDBID }}{{ .Query.IMDBIDShort }}{{else}}{{ .Keywords }}{{end}}"
|
||||
search: "{{ if .Query.IMDBID }}{{ .Query.IMDBID }}{{else}}{{ .Keywords }}{{end}}"
|
||||
# 0 onlyalive 3 rosubbed 4 requested
|
||||
incldead: 1
|
||||
# name, descrname, genre, imdb
|
||||
search_by: "{{ if .Query.IMDBID }}imdb{{else}}name{{end}}"
|
||||
# by: "{{ .Config.sort }}"
|
||||
# ord: "{{ .Config.type }}"
|
||||
|
163
src/Jackett.Common/Definitions/lastfiles.yml
Normal file
163
src/Jackett.Common/Definitions/lastfiles.yml
Normal file
@@ -0,0 +1,163 @@
|
||||
---
|
||||
site: lastfiles
|
||||
name: LastFiles
|
||||
description: "LastFiles (LF) is a ROMANIAN Private Torrent Tracker for 0DAY / GENERAL"
|
||||
language: ro-ro
|
||||
type: private
|
||||
encoding: windows-1252
|
||||
links:
|
||||
- http://last-torrents.org/ # site does not support https ERR_CONNECTION_REFUSED
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 31, cat: Movies/UHD, desc: "Movies/4K"}
|
||||
- {id: 5, cat: Movies/BluRay, desc: "Movies/BluRay"}
|
||||
- {id: 9, cat: Movies/DVD, desc: "Movies/DVD"}
|
||||
- {id: 11, cat: Movies/HD, desc: "Movies/HD"}
|
||||
- {id: 58, cat: Movies/SD, desc: "Movies/Cam"}
|
||||
- {id: 61, cat: Movies/3D, desc: "Movies/3D"}
|
||||
- {id: 24, cat: Movies/SD, desc: "Movies/XVID"}
|
||||
- {id: 16, cat: Movies, desc: "Old Movies"}
|
||||
- {id: 15, cat: Movies, desc: "Movies Mobile"}
|
||||
- {id: 54, cat: Movies/WEBDL, desc: "Movies/WEB-DL"}
|
||||
- {id: 56, cat: TV/Documentary, desc: "Documentary"}
|
||||
- {id: 18, cat: Movies, desc: "Movie Packs"}
|
||||
- {id: 32, cat: Movies/UHD, desc: "Movies/4K-RO"}
|
||||
- {id: 6, cat: Movies/BluRay, desc: "Movies/BluRay-Ro"}
|
||||
- {id: 12, cat: Movies/HD, desc: "Movies/HD-Ro"}
|
||||
- {id: 81, cat: Movies/HD, desc: "Movies/x265-Ro"}
|
||||
- {id: 82, cat: Movies/HD, desc: "Movies/x265"}
|
||||
- {id: 10, cat: Movies/DVD, desc: "Movies/DVD-Ro"}
|
||||
- {id: 59, cat: Movies/SD, desc: "Movies/Cam-Ro"}
|
||||
- {id: 25, cat: Movies/SD, desc: "Movies/XVID-Ro"}
|
||||
- {id: 17, cat: Movies, desc: "Old Movies Ro"}
|
||||
- {id: 55, cat: Movies/WEBDL, desc: "Movies/WEB-DL Ro"}
|
||||
- {id: 57, cat: TV/Documentary, desc: "Documentary-Ro"}
|
||||
- {id: 62, cat: Movies/3D, desc: "Movies/3D-RO"}
|
||||
- {id: 19, cat: Movies, desc: "Movie Packs Ro"}
|
||||
- {id: 20, cat: TV, desc: "TV Episodes"}
|
||||
- {id: 21, cat: TV, desc: "TV Episodes Ro"}
|
||||
- {id: 13, cat: TV/HD, desc: "HDTV"}
|
||||
- {id: 14, cat: TV/HD, desc: "HDTV-Ro"}
|
||||
- {id: 28, cat: Audio, desc: "Music"}
|
||||
- {id: 51, cat: PC/Phone-Android, desc: "Android Apps & Games"}
|
||||
- {id: 26, cat: PC/0day, desc: "Software"}
|
||||
- {id: 30, cat: PC/ISO, desc: "Games PC-ISO"}
|
||||
- {id: 33, cat: PC/Games, desc: "Game Packs"}
|
||||
- {id: 1, cat: TV/Anime, desc: "Anime/Hentai"}
|
||||
- {id: 2, cat: TV/Anime, desc: "Anime-Ro"}
|
||||
- {id: 42, cat: TV/Sport, desc: "Sport"}
|
||||
- {id: 43, cat: Books, desc: "Books"}
|
||||
- {id: 44, cat: Other, desc: "Images"}
|
||||
- {id: 49, cat: Other, desc: "Diverse"}
|
||||
- {id: 22, cat: Other, desc: "RoContent"}
|
||||
- {id: 60, cat: XXX, desc: "Images/XXX"}
|
||||
- {id: 27, cat: XXX, desc: "XXX"}
|
||||
|
||||
modes:
|
||||
search: [q, imdbid]
|
||||
tv-search: [q, season, ep, imdbid]
|
||||
movie-search: [q, imdbid]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
- name: sort
|
||||
type: select
|
||||
label: Sort requested from site
|
||||
default: "4"
|
||||
options:
|
||||
"4": "created"
|
||||
"7": "seeders"
|
||||
"5": "size"
|
||||
"1": "title"
|
||||
- name: type
|
||||
type: select
|
||||
label: Order requested from site
|
||||
default: "desc"
|
||||
options:
|
||||
"desc": "desc"
|
||||
"asc": "asc"
|
||||
|
||||
login:
|
||||
path: takelogin.php
|
||||
method: post
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
error:
|
||||
- selector: td.embedded:has(h2:contains("failed"))
|
||||
test:
|
||||
path: browse.php
|
||||
|
||||
ratio:
|
||||
path: browse.php
|
||||
selector: font:contains("Ratio:") ~ font
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: browse.php
|
||||
inputs:
|
||||
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
|
||||
search: "{{ if .Query.IMDBID }}{{ .Query.IMDBID }}{{else}}{{ .Keywords }}{{end}}"
|
||||
# 0 name 1 genre 2 descr
|
||||
genre: "{{ if .Query.IMDBID }}2{{else}}0{{end}}"
|
||||
# 0 active 1 incldead 2 onlydead 3 free 4 double
|
||||
incldead: 1
|
||||
sort: "{{ .Config.sort }}"
|
||||
type: "{{ .Config.type }}"
|
||||
|
||||
rows:
|
||||
selector: table.browser > tbody > tr:has(a[href^="download.php/"])
|
||||
|
||||
fields:
|
||||
category:
|
||||
selector: a[href^="browse.php?cat="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
title:
|
||||
selector: a[href^="details.php?id="]
|
||||
details:
|
||||
selector: a[href^="details.php?id="]
|
||||
attribute: href
|
||||
banner:
|
||||
selector: a[href^="details.php?id="]
|
||||
attribute: onmouseover
|
||||
filters:
|
||||
- name: regexp
|
||||
args: src=([^\s]+)
|
||||
download:
|
||||
selector: a[href^="download.php/"]
|
||||
attribute: href
|
||||
date:
|
||||
selector: td:nth-child(3)
|
||||
filters:
|
||||
- name: dateparse
|
||||
args: "02-01-200615:04:05"
|
||||
size:
|
||||
selector: td:nth-child(4)
|
||||
grabs:
|
||||
selector: td:nth-child(5)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: (\d+)
|
||||
seeders:
|
||||
selector: td:nth-child(6)
|
||||
leechers:
|
||||
selector: td:nth-child(7)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"span:contains(\"FREE\")": 0
|
||||
"span:contains(\"Half\")": 0.5
|
||||
"*": 1
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"span:contains(\"2XUP\")": 2
|
||||
"*": 1
|
||||
# engine n/a
|
@@ -40,6 +40,13 @@
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
- name: 2facode
|
||||
type: text
|
||||
label: 2FA code
|
||||
- name: info_2fa
|
||||
type: info
|
||||
label: "About 2FA code"
|
||||
default: "Only fill in the <b>2FA code</b> box if you have enabled <b>2FA</b> on the MoeCat Web Site. Otherwise just leave it empty."
|
||||
- name: sort
|
||||
type: select
|
||||
label: Sort requested from site
|
||||
@@ -65,10 +72,12 @@
|
||||
logintype: username
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
scode: "{{ .Config.2facode }}"
|
||||
thispagewidth: yes
|
||||
logout: 720
|
||||
error:
|
||||
- selector: td.embedded:has(h2:contains("姿势不正确"))
|
||||
- selector: td.embedded:has(h2:contains("登录失败"))
|
||||
test:
|
||||
path: index.php
|
||||
selector: a[href="logout.php"]
|
||||
|
@@ -23,9 +23,9 @@
|
||||
- {id: 13, cat: XXX, desc: "XXX"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
search: [q, imdbid]
|
||||
tv-search: [q, season, ep, imdbid]
|
||||
movie-search: [q, imdbid]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
@@ -69,13 +69,14 @@
|
||||
test:
|
||||
path: browse.php
|
||||
selector: a[href^="logout.php"]
|
||||
|
||||
|
||||
search:
|
||||
paths:
|
||||
# https://polishsource.cz/browse.php?search=tt1598778&incldead=1&scene=0&pl=0&sub=&search_in=nfo
|
||||
- path: browse.php
|
||||
inputs:
|
||||
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
search: "{{ if .Query.IMDBID }}{{ .Query.IMDBID }}{{else}}{{ .Keywords }}{{end}}"
|
||||
# 0 onlyactive 1 all 2 onlydead
|
||||
incldead: 1
|
||||
# 0 all 1 scene 2 notscene 3 internal
|
||||
@@ -85,7 +86,7 @@
|
||||
# subcat blank=all
|
||||
sub: ""
|
||||
# title both nfo
|
||||
search_in: title
|
||||
search_in: "{{ if .Query.IMDBID }}nfo{{else}}title{{end}}"
|
||||
sort: "{{ .Config.sort }}"
|
||||
type: "{{ .Config.type }}"
|
||||
|
||||
|
@@ -18,7 +18,7 @@
|
||||
- {id: 55, cat: Books, desc: "Comics"}
|
||||
- {id: 5, cat: Movies/HD, desc: "Movies/720p HD"}
|
||||
- {id: 8, cat: Movies/HD, desc: "Movies/1080p HD"}
|
||||
- {id: 15, cat: Movies/HD, desc: "Movies/Ultra-HD"}
|
||||
- {id: 15, cat: Movies/UHD, desc: "Movies/Ultra-HD"}
|
||||
- {id: 44, cat: Movies, desc: "Movies/Packs"}
|
||||
- {id: 69, cat: Books, desc: "E Books"}
|
||||
- {id: 12, cat: Books, desc: "E Learning"}
|
||||
@@ -35,9 +35,33 @@
|
||||
- {id: 9, cat: XXX, desc: "XXX"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
search: [q, imdbid]
|
||||
tv-search: [q, season, ep, imdbid]
|
||||
movie-search: [q, imdbid]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
- name: sort
|
||||
type: select
|
||||
label: Sort requested from site
|
||||
default: "4"
|
||||
options:
|
||||
"4": "created"
|
||||
"7": "seeders"
|
||||
"5": "size"
|
||||
"1": "title"
|
||||
- name: type
|
||||
type: select
|
||||
label: Order requested from site
|
||||
default: "desc"
|
||||
options:
|
||||
"desc": "desc"
|
||||
"asc": "asc"
|
||||
|
||||
login:
|
||||
path: loginproc/
|
||||
@@ -55,16 +79,22 @@
|
||||
|
||||
search:
|
||||
paths:
|
||||
# https://ptfiles.net/browse.php?search=tt3612126&incldead=1&title=1
|
||||
- path: browse.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
|
||||
search: "{{ if .Query.IMDBID }}{{ .Query.IMDBID }}{{else}}{{ .Keywords }}{{end}}"
|
||||
# 0 active 1 both 2 dead 3 needseed
|
||||
incldead: 0
|
||||
onlyname: 1
|
||||
onlyname2: true
|
||||
# 0 title 1 descr 2 both 3 genre
|
||||
title: "{{ if .Query.IMDBID }}1{{else}}0{{end}}"
|
||||
sort: "{{ .Config.sort }}"
|
||||
type: "{{ .Config.type }}"
|
||||
|
||||
rows:
|
||||
# for some users (don't know why) the table is called tablethree instead of tortable
|
||||
selector: table#tortable > tbody > tr.rowhead, table#tablethree > tbody > tr.rowhead
|
||||
|
||||
fields:
|
||||
# column 3 will be the wait time for new users
|
||||
download:
|
||||
@@ -124,3 +154,4 @@
|
||||
filters:
|
||||
- name: trim
|
||||
args: "/"
|
||||
# TBDev Custom
|
||||
|
@@ -108,7 +108,7 @@
|
||||
- path: selection.php
|
||||
inputs:
|
||||
$raw: "{{ if .Categories }}{{ range .Categories }}scat[]={{.}}&{{end}}{{else}}{{end}}"
|
||||
search: "{{ if .Query.IMDBID }}{{else}}{{ .Keywords }}{{end}}"
|
||||
search: "{{ if .Query.IMDBID }}{{ .Query.IMDBID }}{{else}}{{ .Keywords }}{{end}}"
|
||||
# 0 name 1 descr 2 both 4 imdb
|
||||
blah: "{{ if .Query.IMDBID }}4{{else}}0{{end}}"
|
||||
orderby: "{{ .Config.sort }}"
|
||||
|
@@ -10,7 +10,7 @@
|
||||
legacylinks:
|
||||
- https://torrent-turk.org/
|
||||
- https://torrent-turk.co/
|
||||
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 149, cat: Movies, desc: "Movies/Turkish"}
|
||||
@@ -27,7 +27,7 @@
|
||||
- {id: 161, cat: Movies/DVD, desc: "Movies/Foreign/DVD5-DVD9"}
|
||||
- {id: 162, cat: Movies/WEBDL, desc: "Movies/Foreign/BRRip-HDRip-DVDRip-WebDL"}
|
||||
- {id: 163, cat: Movies/Other, desc: "Movies/Foreign/Boxset"}
|
||||
|
||||
|
||||
- {id: 164, cat: TV, desc: "TV"}
|
||||
- {id: 165, cat: TV, desc: "TV/Turkish"}
|
||||
- {id: 166, cat: TV, desc: "TV/Foreign"}
|
||||
@@ -35,7 +35,7 @@
|
||||
- {id: 185, cat: TV/Documentary, desc: "TV/Documentary"}
|
||||
- {id: 168, cat: TV/Other, desc: "TV/Other"}
|
||||
- {id: 169, cat: TV/Other, desc: "TV/Boxset"}
|
||||
|
||||
|
||||
- {id: 191, cat: TV, desc: "TV/BluTv"}
|
||||
- {id: 192, cat: TV, desc: "TV/BluTv Series"}
|
||||
- {id: 193, cat: Movies, desc: "TV/BluTv Film"}
|
||||
@@ -57,12 +57,12 @@
|
||||
- {id: 179, cat: PC, desc: "OS"}
|
||||
- {id: 180, cat: PC/Games, desc: "PC/Games"}
|
||||
- {id: 181, cat: Console, desc: "Playstation"}
|
||||
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
@@ -89,7 +89,11 @@
|
||||
options:
|
||||
"desc": "desc"
|
||||
"asc": "asc"
|
||||
|
||||
- name: info_results
|
||||
type: info
|
||||
label: Search results
|
||||
default: "If you are getting the error: <b>An error has occured!You can't view this page unless you read all your unread messages</b>, then access the site with your browser and <b>mark as read</b> all PMs."
|
||||
|
||||
login:
|
||||
path: ?p=home&pid=1
|
||||
method: form
|
||||
@@ -111,11 +115,11 @@
|
||||
test:
|
||||
path: ?p=torrents&type=bookmarks&pid=508
|
||||
selector: a#logout
|
||||
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: /
|
||||
keywordsfilters:
|
||||
keywordsfilters:
|
||||
- name: re_replace
|
||||
args: ["[^a-zA-Z0-9]+", "%25"]
|
||||
inputs:
|
||||
@@ -129,6 +133,7 @@
|
||||
"sortOptions[sortOrder]": "{{ .Config.type }}"
|
||||
error:
|
||||
- selector: div.error:not(:contains("Hiçbir sonuç bulunamadı."))
|
||||
# does not support imdbid search and does not return imdb link in results.
|
||||
|
||||
rows:
|
||||
selector: table#torrents_table_classic > tbody > tr:has(td.torrent_name)
|
||||
@@ -150,7 +155,7 @@
|
||||
attribute: href
|
||||
date:
|
||||
optional: true
|
||||
# Uploaded 30-01-2019 15:02 by
|
||||
# Uploaded 30-01-2019 15:02 by
|
||||
selector: td.torrent_name:not(:contains(" at "))
|
||||
filters:
|
||||
- name: regexp
|
||||
|
@@ -195,12 +195,12 @@
|
||||
- name: replace
|
||||
args: ["f6c8a6", "0.75"]
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
img[src$="2x.gif"]: 2
|
||||
img[src$="3x.gif"]: 3
|
||||
img[src$="4x.gif"]: 4
|
||||
img[src$="5x.gif"]: 5
|
||||
img[src$="6x.gif"]: 6
|
||||
img[src$="7x.gif"]: 7
|
||||
"*": 1
|
||||
case:
|
||||
img[src$="2x.gif"]: 2
|
||||
img[src$="3x.gif"]: 3
|
||||
img[src$="4x.gif"]: 4
|
||||
img[src$="5x.gif"]: 5
|
||||
img[src$="6x.gif"]: 6
|
||||
img[src$="7x.gif"]: 7
|
||||
"*": 1
|
||||
# xbtit customised
|
||||
|
@@ -38,9 +38,9 @@
|
||||
- {id: 56, cat: XXX, desc: "XXX"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
search: [q, imdbid]
|
||||
tv-search: [q, season, ep, imdbid]
|
||||
movie-search: [q, imdbid]
|
||||
|
||||
settings:
|
||||
- name: cookie
|
||||
@@ -81,11 +81,11 @@
|
||||
- path: browse.php
|
||||
inputs:
|
||||
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
search: "{{ if .Query.IMDBID }}{{ .Query.IMDBID }}{{else}}{{ .Keywords }}{{end}}"
|
||||
# 0 onlyactive 1 all 2 onlydead 3 free
|
||||
incldead: 1
|
||||
# 0 title 1 descr 2 both
|
||||
titlesearch: 0
|
||||
titlesearch: "{{ if .Query.IMDBID }}1{{else}}0{{end}}"
|
||||
# 0 all 1 polish 2 not polish
|
||||
polish: 0
|
||||
cat_film: ""
|
||||
@@ -114,6 +114,10 @@
|
||||
download:
|
||||
selector: a[href^="download.php/"]
|
||||
attribute: href
|
||||
imdb:
|
||||
optional: true
|
||||
selector: a[href*="www.imdb.com/title/tt"]
|
||||
attribute: href
|
||||
description:
|
||||
text: ""
|
||||
description:
|
||||
|
@@ -1,7 +1,7 @@
|
||||
---
|
||||
site: torrentlt
|
||||
name: Torrent.LT
|
||||
description: "Torrent.LT is Private site for TV / MOVIES / GENERAL"
|
||||
description: "Torrent.LT is a LITHUANIAN Private Torrent Tracker for 0DAY / GENERAL"
|
||||
language: lt-lt
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
@@ -10,12 +10,14 @@
|
||||
legacylinks:
|
||||
- http://www.torrent.ai/
|
||||
- https://torrent.ai/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 27, cat: TV, desc: "Animacija"}
|
||||
- {id: 76, cat: TV, desc: "Animacija / LT"}
|
||||
- {id: 35, cat: TV/Anime, desc: "Anime"}
|
||||
- {id: 31, cat: Movies/DVD, desc: "Filmai / DVD"}
|
||||
- {id: 33, cat: Movies, desc: "Filmai / LTU"}
|
||||
- {id: 33, cat: Movies, desc: "Filmai / LT"}
|
||||
- {id: 43, cat: Movies, desc: "Filmai / LT-Subs"}
|
||||
- {id: 34, cat: Movies, desc: "Filmai / Eng"}
|
||||
- {id: 32, cat: Movies, desc: "Filmai / Rus"}
|
||||
@@ -60,12 +62,12 @@
|
||||
- {id: 71, cat: XXX/Packs, desc: "pr0n / pack"}
|
||||
- {id: 30, cat: Other, desc: "Kita"}
|
||||
- {id: 41, cat: Books, desc: "E-Books"}
|
||||
- {id: 76, cat: TV, desc: "Animacija / LT"}
|
||||
- {id: 77, cat: Other, desc: "Educational"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
|
||||
settings:
|
||||
- name: cookie
|
||||
@@ -75,6 +77,21 @@
|
||||
type: info
|
||||
label: How to get the Cookie
|
||||
default: "<ol><li>Login to this tracker in your browser<li>Open the <b>DevTools</b> panel by pressing <b>F12</b><li>Select the <b>Network</b> tab<li>Click on the <b>Doc</b> button<li>Refresh the page by pressing <b>F5</b><li>Select the <b>Headers</b> tab<li>Find 'cookie:' in the <b>Request Headers</b> section<li>Copy & paste the whole cookie string to here</ol>"
|
||||
- name: sort
|
||||
type: select
|
||||
label: Sort requested from site
|
||||
default: "0"
|
||||
options:
|
||||
"0": "created"
|
||||
"6": "seeders"
|
||||
"4": "size"
|
||||
- name: type
|
||||
type: select
|
||||
label: Order requested from site
|
||||
default: "desc"
|
||||
options:
|
||||
"desc": "desc"
|
||||
"asc": "asc"
|
||||
|
||||
login:
|
||||
method: cookie
|
||||
@@ -87,16 +104,20 @@
|
||||
paths:
|
||||
- path: lt/torrents.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}cats[]={{.}}&{{end}}"
|
||||
$raw: "{{ range .Categories }}cats[]={{.}}&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
sort: "{{ .Config.sort }}"
|
||||
type: "{{ .Config.type }}"
|
||||
keywordsfilters:
|
||||
- name: replace
|
||||
args: [".", " "] # issue #3296
|
||||
|
||||
rows:
|
||||
selector: table> tbody > tr[class^="torrents_table_row_"]
|
||||
filters:
|
||||
- name: andmatch
|
||||
args: 50
|
||||
|
||||
fields:
|
||||
title:
|
||||
selector: td[class$="torrent_info"] a
|
||||
@@ -122,7 +143,9 @@
|
||||
size:
|
||||
selector: td a.torrent_size
|
||||
downloadvolumefactor:
|
||||
text: 1
|
||||
case:
|
||||
img[src$="/freedownload.gif"]: 0
|
||||
"*": 1
|
||||
uploadvolumefactor:
|
||||
text: 1
|
||||
date:
|
||||
@@ -131,4 +154,4 @@
|
||||
filters:
|
||||
- name: dateparse
|
||||
args: "2006-01-02 15:04"
|
||||
# engine tbd
|
||||
# engine n/a
|
||||
|
60
src/Jackett.Common/Definitions/torrentoyunindir.yml
Normal file
60
src/Jackett.Common/Definitions/torrentoyunindir.yml
Normal file
@@ -0,0 +1,60 @@
|
||||
---
|
||||
site: torrentoyunindir
|
||||
name: Torrent Oyun indir
|
||||
description: "Torrent Oyun indir is a TURKISH Public torrent site for GAMES"
|
||||
language: tr
|
||||
type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://www.torrentoyunindir.com/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
1: PC/Games
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
|
||||
settings: []
|
||||
|
||||
download:
|
||||
selector: div.facepaylas a
|
||||
attribute: href
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: "{{ if .Keywords }}?s={{ .Keywords }}{{else}}{{end}}"
|
||||
|
||||
rows:
|
||||
selector: div.moviefilm
|
||||
|
||||
fields:
|
||||
category:
|
||||
text: 1
|
||||
title:
|
||||
selector: div.movief
|
||||
details:
|
||||
selector: a
|
||||
attribute: href
|
||||
download:
|
||||
selector: a
|
||||
attribute: href
|
||||
description:
|
||||
selector: a > span
|
||||
attribute: class
|
||||
banner:
|
||||
selector: img
|
||||
attribute: src
|
||||
date:
|
||||
text: now
|
||||
size:
|
||||
text: "512 MB"
|
||||
seeders:
|
||||
text: 1
|
||||
leechers:
|
||||
text: 1
|
||||
downloadvolumefactor:
|
||||
text: 0
|
||||
uploadvolumefactor:
|
||||
text: 1
|
||||
# engine n/a
|
@@ -7,11 +7,12 @@
|
||||
encoding: UTF-8
|
||||
followredirect: true
|
||||
links:
|
||||
- https://torrentview5.net/
|
||||
- https://torrentview6.net/
|
||||
legacylinks:
|
||||
- https://torrentview.net/
|
||||
- https://torrentview3.net/
|
||||
- https://torrentview4.net/
|
||||
- https://torrentview5.net/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -1,87 +0,0 @@
|
||||
---
|
||||
site: torrentwal
|
||||
name: TorrentWal (토렌트왈)
|
||||
description: "Torrent Wal is a free Korean tracker for Korean media."
|
||||
language: ko-KR
|
||||
type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://torrentwal.com/
|
||||
legacylinks:
|
||||
- https://torrentwal1.com/
|
||||
- https://torrentwal2.com/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: "torrent_movie", cat: Movies, desc: "토렌트영화 (Movies)"}
|
||||
- {id: "torrent_bluray", cat: Movies/BluRay, desc: "토렌트영화 (Movies)"}
|
||||
- {id: "torrent_variety", cat: TV, desc: "TV예능 (TV Variety Shows)"}
|
||||
- {id: "torrent_tv", cat: TV, desc: "TV드라마 (TV Dramas)"}
|
||||
- {id: "torrent_docu", cat: TV/Documentary, desc: "다큐/시사 (Documentaries)"}
|
||||
- {id: "torrent_sports", cat: TV/Sport, desc: "스포츠 (Sports)"}
|
||||
- {id: "torrent_util", cat: PC, desc: "토렌트유틸 (Utilities)"}
|
||||
- {id: "torrent_ani", cat: TV/Anime, desc: "애니메이션 (Anime)"}
|
||||
- {id: "torrent_song", cat: Audio, desc: "해외음원 (Music)"}
|
||||
- {id: "torrent_game", cat: PC/Games, desc: "토렌트게임 (Games)"}
|
||||
- {id: "torrent_mid", cat: TV/FOREIGN, desc: 해외TV (Foreign TV)"}
|
||||
- {id: "torrent_child", cat: TV, desc: "유아/어린이 (Children's)"}
|
||||
- {id: "torrent_etc", cat: Other, desc: "토렌트 기타 (Other)"}
|
||||
- {id: "torrent_iphone", cat: PC/Phone-Other, desc: "휴대기기 (Phone Apps)"}
|
||||
- {id: "torrent_book", cat: Books, desc: "토렌트도서 (Books)"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
|
||||
settings: []
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: "{{ if .Keywords }}bbs/s-1-{{ .Keywords }}{{else}}bbs/s-1-유희열{{end}}"
|
||||
rows:
|
||||
selector: tr.bg1:has(a[href^="/bbs/bc.php?bo_table="])
|
||||
fields:
|
||||
magnet:
|
||||
selector: td:nth-child(1) a[href^="javascript:"]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["javascript:Mag_dn('",""]
|
||||
- name: replace
|
||||
args: ["')",""]
|
||||
- name: prepend
|
||||
args: "magnet:?xt=urn:btih:"
|
||||
seeders:
|
||||
text: 1
|
||||
leechers:
|
||||
text: 1
|
||||
category:
|
||||
selector: td:nth-child(2) a[href^="/bbs/bc.php?bo_table="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: bo_table
|
||||
title:
|
||||
selector: td:nth-child(2) a:last-of-type
|
||||
details:
|
||||
selector: td:nth-child(2) a:last-of-type
|
||||
attribute: href
|
||||
date:
|
||||
selector: td:nth-child(3)
|
||||
filters:
|
||||
- name: dateparse
|
||||
args: "01-02"
|
||||
size:
|
||||
selector: td:nth-child(4):contains(".M")
|
||||
text: "0 "
|
||||
size:
|
||||
selector: td:nth-child(4):not(:contains(".M"))
|
||||
optional: true
|
||||
filters:
|
||||
- name: append
|
||||
args: "B"
|
||||
downloadvolumefactor:
|
||||
text: 0
|
||||
uploadvolumefactor:
|
||||
text: 1
|
@@ -50,9 +50,9 @@
|
||||
- {id: 24, cat: TV, desc: "Teens"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
search: [q, imdbid]
|
||||
tv-search: [q, season, ep, imdbid]
|
||||
movie-search: [q, imdbid]
|
||||
music-search: [q]
|
||||
|
||||
login:
|
||||
@@ -74,10 +74,10 @@
|
||||
- path: index.php
|
||||
inputs:
|
||||
page: torrents
|
||||
search: "{{ if .Keywords }}{{ .Keywords }}{{else}}{{end}}"
|
||||
search: "{{ if .Query.IMDBID }}{{ .Query.IMDBID }}{{else}}{{ .Keywords }}{{end}}"
|
||||
category: "{{ if .Categories }}{{ range .Categories }}{{.}};{{end}}{{else}}0{{end}}"
|
||||
# 0 filename 1 file&descr 2 descr
|
||||
options: 0
|
||||
# 0=title, 1=title&descr, 2=descr
|
||||
options: "{{ if .Query.IMDBID }}2{{else}}0{{end}}"
|
||||
# 0 all 1 activeonly 2 deadonly
|
||||
active: 0
|
||||
order: "{{ .Config.sort }}"
|
||||
|
151
src/Jackett.Common/Definitions/u-torrents.yml
Normal file
151
src/Jackett.Common/Definitions/u-torrents.yml
Normal file
@@ -0,0 +1,151 @@
|
||||
---
|
||||
site: u-torrents
|
||||
name: U-Torrents
|
||||
description: "U-Torrents.ro (UTT) is a ROMANIAN Private Torrent Tracker for 0DAY / GENERAL"
|
||||
language: ro-ro
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://www.u-torrents.ro/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 300, cat: TV, desc: "Cartoons"}
|
||||
- {id: 401, cat: PC/0day, desc: "Appz"}
|
||||
- {id: 42, cat: TV/Documentary, desc: "Documentary"}
|
||||
- {id: 6, cat: Books, desc: "eBooks"}
|
||||
- {id: 11, cat: PC/Games, desc: "Games-PC"}
|
||||
- {id: 19, cat: PC/Phone-Other, desc: "Mobile"}
|
||||
- {id: 1, cat: Movies, desc: "Movies"}
|
||||
- {id: 43, cat: Movies/BluRay, desc: "Movies-BluRay"}
|
||||
- {id: 49, cat: Movies/BluRay, desc: "Movies-BluRay-RO"}
|
||||
- {id: 7, cat: Movies/DVD, desc: "Movies-DVD"}
|
||||
- {id: 200, cat: Movies/DVD, desc: "Movies-DVD-RO"}
|
||||
- {id: 17, cat: Movies/HD, desc: "Movies-HD"}
|
||||
- {id: 45, cat: Movies/HD, desc: "Movies-HD-RO"}
|
||||
- {id: 48, cat: Movies/3D, desc: "Movies-3D"}
|
||||
- {id: 38, cat: Movies, desc: "Movies-Packs"}
|
||||
- {id: 10, cat: Movies/SD, desc: "Movies-XviD"}
|
||||
- {id: 44, cat: Movies/SD, desc: "Movies-XviD-RO"}
|
||||
- {id: 5, cat: Audio/MP3, desc: "Music MP3"}
|
||||
- {id: 23, cat: Audio/Video, desc: "Music Videos"}
|
||||
- {id: 22, cat: TV/Sport, desc: "Sport"}
|
||||
- {id: 20, cat: TV, desc: "TV Episodes"}
|
||||
- {id: 2, cat: TV/HD, desc: "TV Episodes HD"}
|
||||
- {id: 403, cat: TV/HD, desc: "TV Episodes RO"}
|
||||
- {id: 402, cat: TV/HD, desc: "TV Episodes HD-RO"}
|
||||
- {id: 41, cat: TV, desc: "TV-Packs"}
|
||||
- {id: 68, cat: XXX, desc: "XXX"}
|
||||
- {id: 46, cat: XXX/Imageset, desc: "XXX-IMGSet"}
|
||||
- {id: 18, cat: Other, desc: "Pictures"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
- name: sort
|
||||
type: select
|
||||
label: Sort requested from site
|
||||
default: "4"
|
||||
options:
|
||||
"4": "created"
|
||||
"7": "seeders"
|
||||
"5": "size"
|
||||
"1": "title"
|
||||
- name: type
|
||||
type: select
|
||||
label: Order requested from site
|
||||
default: "desc"
|
||||
options:
|
||||
"desc": "desc"
|
||||
"asc": "asc"
|
||||
|
||||
login:
|
||||
path: takelogin.php
|
||||
method: post
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
submitme: X
|
||||
error:
|
||||
- selector: div.card-body:contains("incorrect")
|
||||
test:
|
||||
path: index.php
|
||||
selector: a[href^="logout.php?id="]
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: browse
|
||||
inputs:
|
||||
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
sort: "{{ .Config.sort }}"
|
||||
type: "{{ .Config.type }}"
|
||||
|
||||
rows:
|
||||
selector: table.table > tbody > tr.browse
|
||||
|
||||
fields:
|
||||
category:
|
||||
selector: a[href^="browse.php?cat="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: regexp
|
||||
args: (\d+)$
|
||||
title_alternate:
|
||||
selector: a[href^="download.php/"]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["download.php/\\d+/", ""]
|
||||
- name: replace
|
||||
args: [".torrent", ""]
|
||||
- name: urldecode
|
||||
title:
|
||||
selector: a[href^="details.php?id="]
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["\\[email\\sprotected\\]", "{{ .Result.title_alternate }}"]
|
||||
details:
|
||||
selector: a[href^="details.php?id="]
|
||||
attribute: href
|
||||
download:
|
||||
selector: a[href^="download.php/"]
|
||||
attribute: href
|
||||
banner:
|
||||
selector: a[href^="details.php?id="]
|
||||
attribute: onmouseover
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "src=(.+?) "
|
||||
date:
|
||||
selector: td:nth-child(4)
|
||||
filters:
|
||||
- name: dateparse
|
||||
args: "02-01-200615:04:05"
|
||||
size:
|
||||
selector: td:nth-child(5)
|
||||
grabs:
|
||||
selector: td:nth-child(6)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: (\d+)
|
||||
seeders:
|
||||
selector: td:nth-child(7)
|
||||
leechers:
|
||||
selector: td:nth-child(8)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[src="imagini/YNTdm1Jn_o.png"]: 0
|
||||
"*": 1
|
||||
uploadvolumefactor:
|
||||
text: 1
|
||||
# engine n/a
|
@@ -36,8 +36,31 @@
|
||||
fields:
|
||||
category:
|
||||
text: 1
|
||||
site_date:
|
||||
selector: a[href^="/torrent/"]
|
||||
filters:
|
||||
# date is at the end of the title, so we get it and name it site_date
|
||||
- name: regexp
|
||||
args: "(\\d{4})$"
|
||||
title:
|
||||
selector: a[href^="/torrent/"]
|
||||
attribute: title
|
||||
filters:
|
||||
- name: replace
|
||||
args: [" en Torrent", ""]
|
||||
- name: replace
|
||||
args: [" FRENCH", " {{ .Result.site_date }} FRENCH"]
|
||||
- name: replace
|
||||
args: ["MULTI", "{{ .Result.site_date }} MULTI"]
|
||||
- name: replace
|
||||
args: ["TRUEFRENCH", "{{ .Result.site_date }} TRUEFRENCH"]
|
||||
- name: replace
|
||||
args: ["VOSTFR", "{{ .Result.site_date }} VOSTFR"]
|
||||
# and we delete the year at the end
|
||||
- name: re_replace
|
||||
args: ["(\\w+)$", ""]
|
||||
- name: replace
|
||||
args: ["WEBRIP", "WEBDL"]
|
||||
details:
|
||||
selector: a[href^="/torrent/"]
|
||||
attribute: href
|
||||
|
@@ -24,7 +24,11 @@
|
||||
- {id: 34, cat: Movies/DVD, desc: "Film/DVD-HUN"}
|
||||
- {id: 10, cat: Movies/HD, desc: "Film/HD-DVD/Külf"}
|
||||
- {id: 7, cat: Movies/SD, desc: "Film/SD-X264-HUN"}
|
||||
- {id: 27, cat: Movies/SD, desc: "Film/SD-X264-HUN Mese"}
|
||||
- {id: 25, cat: TV/SD, desc: "Sorozat/SD-HUN"}
|
||||
- {id: 8, cat: Movies/SD, desc: "Film/SD-X264"}
|
||||
- {id: 28, cat: Movies/SD, desc: "Film/SD-X264 Mese/Külf."}
|
||||
- {id: 26, cat: TV/SD, desc: "Sorozat/SD"}
|
||||
- {id: 15, cat: XXX, desc: "Film/XXX-SD"}
|
||||
- {id: 16, cat: XXX, desc: "Film/XXX-HD"}
|
||||
- {id: 40, cat: XXX, desc: "Film/XXX-DVD"}
|
||||
@@ -34,12 +38,8 @@
|
||||
- {id: 45, cat: Console, desc: "Game/Konzol"}
|
||||
- {id: 18, cat: Other, desc: "Képek"}
|
||||
- {id: 17, cat: XXX, desc: "Képek/XXX"}
|
||||
- {id: 27, cat: Books, desc: "Mese/Hun"}
|
||||
- {id: 28, cat: Books, desc: "Mese/Külf."}
|
||||
- {id: 24, cat: PC/Phone-Other, desc: "Program/Mobil"}
|
||||
- {id: 1, cat: PC/0day, desc: "Program/PC"}
|
||||
- {id: 25, cat: TV/SD, desc: "Sorozat/SD-HUN"}
|
||||
- {id: 26, cat: TV/SD, desc: "Sorozat/SD"}
|
||||
- {id: 44, cat: TV/HD, desc: "Sorozat/HD-HUN"}
|
||||
- {id: 46, cat: TV/HD, desc: "Sorozat/HD"}
|
||||
- {id: 48, cat: TV/UHD, desc: "Sorozat/4K-HUN"}
|
||||
@@ -48,9 +48,9 @@
|
||||
- {id: 12, cat: Audio, desc: "Zene/Külf."}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
search: [q, imdbid]
|
||||
tv-search: [q, season, ep, imdbid]
|
||||
movie-search: [q, imdbid]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
@@ -100,8 +100,9 @@
|
||||
args: ["[^a-zA-Z0-9]+", "%"]
|
||||
inputs:
|
||||
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
|
||||
keywords: "{{ .Keywords }}"
|
||||
search_type: t_name
|
||||
keywords: "{{ if .Query.IMDBID }}{{ .Query.IMDBID }}{{else}}{{ .Keywords }}{{end}}"
|
||||
# t_name , t_description , t_both , t_uploader
|
||||
search_type: "{{ if .Query.IMDBID }}t_description{{else}}t_name{{end}}"
|
||||
sort: "{{ .Config.sort }}"
|
||||
type: "{{ .Config.type }}"
|
||||
|
||||
@@ -121,6 +122,10 @@
|
||||
filters:
|
||||
- name: regexp
|
||||
args: img src=\\'(.*?)\\'
|
||||
imdb:
|
||||
optional: true
|
||||
selector: a[href*="www.imdb.com/title/tt"]
|
||||
attribute: href
|
||||
details:
|
||||
selector: a[href*="details.php?id="][onmouseover]
|
||||
attribute: href
|
||||
@@ -151,6 +156,7 @@
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[src="./pic/freedownload.gif"]: 0
|
||||
img[src="./pic/silverdownload.gif"]: 0.5
|
||||
"*": 1
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
|
@@ -5,6 +5,7 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using AngleSharp.Html.Parser;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig;
|
||||
@@ -20,8 +21,7 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
private string LoginUrl => SiteLink + "login.php";
|
||||
private string TakeLoginUrl => SiteLink + "takelogin.php";
|
||||
private string SearchUrl => SiteLink + "torrents.php?";
|
||||
private string DownloadUrl => SiteLink + "download.php?id={0}";
|
||||
private string SearchUrl => SiteLink + "torrents.php";
|
||||
|
||||
private new ConfigurationDataRecaptchaLogin configData
|
||||
{
|
||||
@@ -29,8 +29,9 @@ namespace Jackett.Common.Indexers
|
||||
set => base.configData = value;
|
||||
}
|
||||
|
||||
public BitHdtv(IIndexerConfigurationService configService, WebClient w, Logger l, IProtectionService ps)
|
||||
: base(name: "BIT-HDTV",
|
||||
public BitHdtv(IIndexerConfigurationService configService, WebClient w, Logger l, IProtectionService ps) :
|
||||
base(
|
||||
name: "BIT-HDTV",
|
||||
description: "BIT-HDTV - Home of High Definition",
|
||||
link: "https://www.bit-hdtv.com/",
|
||||
caps: new TorznabCapabilities(),
|
||||
@@ -43,9 +44,8 @@ namespace Jackett.Common.Indexers
|
||||
Encoding = Encoding.GetEncoding("iso-8859-1");
|
||||
Language = "en-us";
|
||||
Type = "private";
|
||||
|
||||
TorznabCaps.SupportsImdbMovieSearch = true;
|
||||
|
||||
TorznabCaps.SupportsImdbTVSearch = true;
|
||||
AddCategoryMapping(1, TorznabCatType.TVAnime); // Anime
|
||||
AddCategoryMapping(2, TorznabCatType.MoviesBluRay); // Blu-ray
|
||||
AddCategoryMapping(4, TorznabCatType.TVDocumentary); // Documentaries
|
||||
@@ -76,13 +76,12 @@ namespace Jackett.Common.Indexers
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
LoadValuesFromJson(configJson);
|
||||
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "g-recaptcha-response", configData.Captcha.Value },
|
||||
var pairs = new Dictionary<string, string>
|
||||
{
|
||||
{"username", configData.Username.Value},
|
||||
{"password", configData.Password.Value},
|
||||
{"g-recaptcha-response", configData.Captcha.Value}
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(configData.Captcha.Cookie))
|
||||
{
|
||||
// Cookie was manually supplied
|
||||
@@ -92,7 +91,6 @@ namespace Jackett.Common.Indexers
|
||||
var results = await PerformQuery(new TorznabQuery());
|
||||
if (!results.Any())
|
||||
throw new Exception("Your cookie did not work");
|
||||
|
||||
IsConfigured = true;
|
||||
SaveConfig();
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
@@ -104,17 +102,12 @@ namespace Jackett.Common.Indexers
|
||||
}
|
||||
}
|
||||
|
||||
var response = await RequestLoginAndFollowRedirect(TakeLoginUrl, pairs, null, true, null, SiteLink);
|
||||
await ConfigureIfOK(response.Cookies, response.Content != null && response.Content.Contains("logout.php"), () =>
|
||||
var response = await RequestLoginAndFollowRedirect(TakeLoginUrl, pairs, null, true, referer: SiteLink);
|
||||
await ConfigureIfOK(response.Cookies, response.Content?.Contains("logout.php") == true, () =>
|
||||
{
|
||||
var parser = new HtmlParser();
|
||||
var dom = parser.ParseDocument(response.Content);
|
||||
var messageEl = dom.QuerySelectorAll("table.detail td.text").Last();
|
||||
foreach (var child in messageEl.QuerySelectorAll("a"))
|
||||
child.Remove();
|
||||
foreach (var child in messageEl.QuerySelectorAll("style"))
|
||||
child.Remove();
|
||||
var errorMessage = messageEl.TextContent.Trim();
|
||||
var errorMessage = dom.QuerySelector("table.detail td.text").FirstChild.TextContent.Trim();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
@@ -123,95 +116,97 @@ namespace Jackett.Common.Indexers
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
var qc = new NameValueCollection();
|
||||
if (!string.IsNullOrWhiteSpace(query.ImdbID))
|
||||
var cats = MapTorznabCapsToTrackers(query, true);
|
||||
var qc = new NameValueCollection
|
||||
{
|
||||
{"cat", cats.Count == 1 ? cats[0] : "0"}
|
||||
};
|
||||
var results = new List<WebClientStringResult>();
|
||||
var search = new UriBuilder(SearchUrl);
|
||||
if (query.IsImdbQuery)
|
||||
{
|
||||
qc.Add("search", query.ImdbID);
|
||||
qc.Add("options", "4");
|
||||
qc.Add("options", "4"); //Search URL field for IMDB link
|
||||
search.Query = qc.GetQueryString();
|
||||
results.Add(await RequestStringWithCookiesAndRetry(search.ToString()));
|
||||
qc["Options"] = "1"; //Search Title and Description
|
||||
search.Query = qc.GetQueryString();
|
||||
results.Add(await RequestStringWithCookiesAndRetry(search.ToString()));
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(query.GetQueryString()))
|
||||
else
|
||||
{
|
||||
//Site handles empty string on search param. No need to check for IsNullOrEmpty()
|
||||
qc.Add("search", query.GetQueryString());
|
||||
qc.Add("options", "0");
|
||||
qc.Add("options", "0"); //Search Title Only
|
||||
search.Query = qc.GetQueryString();
|
||||
results.Add(await RequestStringWithCookiesAndRetry(search.ToString()));
|
||||
}
|
||||
var searchUrl = SearchUrl + qc.GetQueryString();
|
||||
|
||||
var trackerCats = MapTorznabCapsToTrackers(query, mapChildrenCatsToParent: true);
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
try
|
||||
{
|
||||
var parser = new HtmlParser();
|
||||
var dom = parser.ParseDocument(results.Content);
|
||||
foreach (var child in dom.QuerySelectorAll("#needseed"))
|
||||
child.Remove();
|
||||
foreach (var table in dom.QuerySelectorAll("table[align=center] + br + table > tbody"))
|
||||
var parser = new HtmlParser();
|
||||
foreach (var result in results)
|
||||
try
|
||||
{
|
||||
var rows = table.Children;
|
||||
foreach (var row in rows.Skip(1))
|
||||
var dom = parser.ParseDocument(result.Content);
|
||||
foreach (var child in dom.QuerySelectorAll("#needseed"))
|
||||
child.Remove();
|
||||
var table = dom.QuerySelector("table[align=center] + br + table > tbody");
|
||||
if (table == null) // No results, so skip this search
|
||||
continue;
|
||||
foreach (var row in table.Children.Skip(1))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
var qLink = row.Children[2].QuerySelector("a");
|
||||
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800; // 48 hours
|
||||
release.Title = qLink.GetAttribute("title");
|
||||
if (!query.MatchQueryStringAND(release.Title))
|
||||
var detailsLink = new Uri(qLink.GetAttribute("href"));
|
||||
//Skip irrelevant and duplicate entries
|
||||
if (!query.MatchQueryStringAND(release.Title) || releases.Any(r => r.Guid == detailsLink))
|
||||
continue;
|
||||
release.Files = ParseUtil.CoerceLong(row.QuerySelector("td:nth-child(4)").TextContent);
|
||||
release.Grabs = ParseUtil.CoerceLong(row.QuerySelector("td:nth-child(8)").TextContent);
|
||||
release.Guid = new Uri(qLink.GetAttribute("href"));
|
||||
release.Files = ParseUtil.CoerceLong(row.Children[3].TextContent);
|
||||
release.Grabs = ParseUtil.CoerceLong(row.Children[7].TextContent);
|
||||
release.Guid = detailsLink;
|
||||
release.Comments = release.Guid;
|
||||
release.Link = new Uri(string.Format(DownloadUrl, qLink.GetAttribute("href").Split('=')[1]));
|
||||
|
||||
var catUrl = row.Children[1].FirstElementChild.GetAttribute("href");
|
||||
var catNum = catUrl.Split('=', '&')[1];
|
||||
release.Link = new Uri(SiteLink + row.QuerySelector("a[href^=\"download.php\"]").GetAttribute("href"));
|
||||
var catUrl = new Uri(SiteLink + row.Children[1].FirstElementChild.GetAttribute("href"));
|
||||
var catQuery = HttpUtility.ParseQueryString(catUrl.Query);
|
||||
var catNum = catQuery["cat"];
|
||||
release.Category = MapTrackerCatToNewznab(catNum);
|
||||
|
||||
// This tracker cannot search multiple cats at a time, so search all cats then filter out results from different cats
|
||||
if (trackerCats.Count > 0 && !trackerCats.Contains(catNum))
|
||||
continue;
|
||||
|
||||
var dateString = row.Children[5].TextContent.Trim();
|
||||
var pubDate = DateTime.ParseExact(dateString, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture);
|
||||
release.PublishDate = DateTime.SpecifyKind(pubDate, DateTimeKind.Local);
|
||||
|
||||
var sizeStr = row.Children[6].TextContent;
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(row.Children[8].TextContent.Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(row.Children[9].TextContent.Trim()) + release.Seeders;
|
||||
switch (row.GetAttribute("bgcolor"))
|
||||
{
|
||||
case "#DDDDDD":
|
||||
release.DownloadVolumeFactor = 1;
|
||||
release.UploadVolumeFactor = 2;
|
||||
break;
|
||||
case "#FFFF99":
|
||||
release.DownloadVolumeFactor = 0;
|
||||
release.UploadVolumeFactor = 1;
|
||||
break;
|
||||
case "#CCFF99":
|
||||
release.DownloadVolumeFactor = 0;
|
||||
release.UploadVolumeFactor = 2;
|
||||
break;
|
||||
default:
|
||||
release.DownloadVolumeFactor = 1;
|
||||
release.UploadVolumeFactor = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
var bgcolor = row.GetAttribute("bgcolor");
|
||||
if (bgcolor == "#DDDDDD")
|
||||
{
|
||||
release.DownloadVolumeFactor = 1;
|
||||
release.UploadVolumeFactor = 2;
|
||||
}
|
||||
else if (bgcolor == "#FFFF99")
|
||||
{
|
||||
release.DownloadVolumeFactor = 0;
|
||||
release.UploadVolumeFactor = 1;
|
||||
}
|
||||
else if (bgcolor == "#CCFF99")
|
||||
{
|
||||
release.DownloadVolumeFactor = 0;
|
||||
release.UploadVolumeFactor = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
release.DownloadVolumeFactor = 1;
|
||||
release.UploadVolumeFactor = 1;
|
||||
}
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(result.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
@@ -990,7 +990,7 @@ namespace Jackett.Common.Indexers
|
||||
try
|
||||
{
|
||||
var Date = DateTimeUtil.ParseDateTimeGoLang(Data, layout);
|
||||
Data = Date.ToString(DateTimeUtil.RFC1123ZPattern);
|
||||
Data = Date.ToString(DateTimeUtil.Rfc1123ZPattern);
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
@@ -1056,10 +1056,10 @@ namespace Jackett.Common.Indexers
|
||||
break;
|
||||
case "timeago":
|
||||
case "reltime":
|
||||
Data = DateTimeUtil.FromTimeAgo(Data).ToString(DateTimeUtil.RFC1123ZPattern);
|
||||
Data = DateTimeUtil.FromTimeAgo(Data).ToString(DateTimeUtil.Rfc1123ZPattern);
|
||||
break;
|
||||
case "fuzzytime":
|
||||
Data = DateTimeUtil.FromUnknown(Data).ToString(DateTimeUtil.RFC1123ZPattern);
|
||||
Data = DateTimeUtil.FromUnknown(Data).ToString(DateTimeUtil.Rfc1123ZPattern);
|
||||
break;
|
||||
case "validfilename":
|
||||
Data = StringUtil.MakeValidFileName(Data, '_', false);
|
||||
@@ -1509,7 +1509,7 @@ namespace Jackett.Common.Indexers
|
||||
break;
|
||||
case "date":
|
||||
release.PublishDate = DateTimeUtil.FromUnknown(value);
|
||||
value = release.PublishDate.ToString(DateTimeUtil.RFC1123ZPattern);
|
||||
value = release.PublishDate.ToString(DateTimeUtil.Rfc1123ZPattern);
|
||||
break;
|
||||
case "files":
|
||||
release.Files = ParseUtil.CoerceLong(value);
|
||||
|
@@ -16,21 +16,15 @@ using NLog;
|
||||
|
||||
namespace Jackett.Common.Indexers
|
||||
{
|
||||
// ReSharper disable once UnusedType.Global
|
||||
internal class EliteTracker : BaseWebIndexer
|
||||
{
|
||||
private string LoginUrl => SiteLink + "takelogin.php";
|
||||
private string BrowseUrl => SiteLink + "browse.php";
|
||||
private bool TorrentHTTPSMode => configData.TorrentHTTPSMode.Value;
|
||||
private string ReplaceMulti => configData.ReplaceMulti.Value;
|
||||
private new ConfigurationDataEliteTracker configData
|
||||
|
||||
{
|
||||
get => (ConfigurationDataEliteTracker)base.configData;
|
||||
set => base.configData = value;
|
||||
}
|
||||
private new ConfigurationDataEliteTracker configData => (ConfigurationDataEliteTracker)base.configData;
|
||||
|
||||
public EliteTracker(IIndexerConfigurationService configService, WebClient webClient, Logger logger, IProtectionService protectionService)
|
||||
: base(name: "Elite-Tracker",
|
||||
: base("Elite-Tracker",
|
||||
description: "French Torrent Tracker",
|
||||
link: "https://elite-tracker.net/",
|
||||
caps: new TorznabCapabilities(),
|
||||
@@ -45,97 +39,100 @@ namespace Jackett.Common.Indexers
|
||||
Language = "fr-fr";
|
||||
Type = "private";
|
||||
|
||||
// Clean capabilities
|
||||
TorznabCaps.Categories.Clear();
|
||||
TorznabCaps.SupportsImdbMovieSearch = true;
|
||||
TorznabCaps.SupportsImdbTVSearch = true;
|
||||
|
||||
AddCategoryMapping(27, TorznabCatType.TVAnime, "Animation/Animes");
|
||||
AddCategoryMapping(63, TorznabCatType.TVAnime, "Animes DVD");
|
||||
AddCategoryMapping(56, TorznabCatType.TVAnime, "Animes HD");
|
||||
AddCategoryMapping(59, TorznabCatType.TVAnime, "Animes Serie");
|
||||
AddCategoryMapping(89, TorznabCatType.TVAnime, "Animes HDLight");
|
||||
AddCategoryMapping(87, TorznabCatType.TVAnime, "Animes Pack");
|
||||
AddCategoryMapping(88, TorznabCatType.TVAnime, "Animes SD");
|
||||
AddCategoryMapping(90, TorznabCatType.TVAnime, "Animes 3D");
|
||||
AddCategoryMapping(90, TorznabCatType.TVAnime, "Animes - 3D");
|
||||
AddCategoryMapping(99, TorznabCatType.TVAnime, "Animes - 4K");
|
||||
AddCategoryMapping(63, TorznabCatType.TVAnime, "Animes - DVD");
|
||||
AddCategoryMapping(56, TorznabCatType.TVAnime, "Animes - HD");
|
||||
AddCategoryMapping(89, TorznabCatType.TVAnime, "Animes - HDRip");
|
||||
AddCategoryMapping(87, TorznabCatType.TVAnime, "Animes - Pack");
|
||||
AddCategoryMapping(88, TorznabCatType.TVAnime, "Animes - SD");
|
||||
AddCategoryMapping(59, TorznabCatType.TVAnime, "Animes - Serie");
|
||||
|
||||
AddCategoryMapping(3, TorznabCatType.PC0day, "APPLICATION");
|
||||
AddCategoryMapping(74, TorznabCatType.PCPhoneAndroid, "ANDROID");
|
||||
AddCategoryMapping(57, TorznabCatType.PCPhoneIOS, "IPHONE");
|
||||
AddCategoryMapping(6, TorznabCatType.PC0day, "LINUX");
|
||||
AddCategoryMapping(5, TorznabCatType.PCMac, "MAC");
|
||||
AddCategoryMapping(4, TorznabCatType.PC0day, "WINDOWS");
|
||||
AddCategoryMapping(74, TorznabCatType.PCPhoneAndroid, "APPLICATION - ANDROID");
|
||||
AddCategoryMapping(57, TorznabCatType.PCPhoneIOS, "APPLICATION - IPHONE");
|
||||
AddCategoryMapping(6, TorznabCatType.PC0day, "APPLICATION - LINUX");
|
||||
AddCategoryMapping(5, TorznabCatType.PCMac, "APPLICATION - MAC");
|
||||
AddCategoryMapping(4, TorznabCatType.PC0day, "APPLICATION - WINDOWS");
|
||||
|
||||
AddCategoryMapping(38, TorznabCatType.TVDocumentary, "DOCUMENTAIRES");
|
||||
AddCategoryMapping(97, TorznabCatType.TVDocumentary, "DOCUMENTAIRES PACK");
|
||||
AddCategoryMapping(97, TorznabCatType.TVDocumentary, "DOCUMENTAIRES - PACK");
|
||||
|
||||
AddCategoryMapping(34, TorznabCatType.Books, "EBOOKS");
|
||||
AddCategoryMapping(86, TorznabCatType.Books, "ABOOKS");
|
||||
AddCategoryMapping(86, TorznabCatType.Books, "EBOOKS - ABOOKS");
|
||||
|
||||
AddCategoryMapping(7, TorznabCatType.Movies, "FILMS");
|
||||
AddCategoryMapping(11, TorznabCatType.MoviesDVD, "DVD");
|
||||
AddCategoryMapping(10, TorznabCatType.MoviesSD, "DVD-RIP/BD-RIP");
|
||||
AddCategoryMapping(53, TorznabCatType.MoviesSD, "DVD-SCREENER");
|
||||
AddCategoryMapping(9, TorznabCatType.MoviesDVD, "R5");
|
||||
AddCategoryMapping(8, TorznabCatType.MoviesSD, "SCREENER");
|
||||
AddCategoryMapping(40, TorznabCatType.Movies, "VO");
|
||||
AddCategoryMapping(39, TorznabCatType.Movies, "VOSTFR");
|
||||
AddCategoryMapping(48, TorznabCatType.MoviesHD, "HD");
|
||||
AddCategoryMapping(51, TorznabCatType.MoviesHD, "1080P");
|
||||
AddCategoryMapping(70, TorznabCatType.Movies3D, "3D");
|
||||
AddCategoryMapping(50, TorznabCatType.MoviesHD, "720P");
|
||||
AddCategoryMapping(84, TorznabCatType.MoviesUHD, "4K");
|
||||
AddCategoryMapping(49, TorznabCatType.MoviesBluRay, "BluRay");
|
||||
AddCategoryMapping(78, TorznabCatType.MoviesHD, "HDLight");
|
||||
AddCategoryMapping(85, TorznabCatType.MoviesHD, "x265");
|
||||
AddCategoryMapping(91, TorznabCatType.Movies3D, "3D");
|
||||
AddCategoryMapping(95, TorznabCatType.Movies, "VOSTFR");
|
||||
AddCategoryMapping(48, TorznabCatType.MoviesHD, "FiLMS HD");
|
||||
AddCategoryMapping(51, TorznabCatType.MoviesHD, "FiLMS HD - 1080p");
|
||||
AddCategoryMapping(98, TorznabCatType.MoviesUHD, "FiLMS HD - 2160p");
|
||||
AddCategoryMapping(70, TorznabCatType.Movies3D, "FiLMS HD - 3D");
|
||||
AddCategoryMapping(84, TorznabCatType.MoviesUHD, "FiLMS HD - 4K");
|
||||
AddCategoryMapping(50, TorznabCatType.MoviesHD, "FiLMS HD - 720P");
|
||||
AddCategoryMapping(49, TorznabCatType.MoviesBluRay, "FiLMS HD - BluRay");
|
||||
AddCategoryMapping(78, TorznabCatType.MoviesHD, "FiLMS HD - HDRip");
|
||||
AddCategoryMapping(95, TorznabCatType.Movies, "FiLMS HD - VOSTFR");
|
||||
AddCategoryMapping(85, TorznabCatType.MoviesHD, "FiLMS HD - x265");
|
||||
|
||||
AddCategoryMapping(7, TorznabCatType.Movies, "FiLMS SD");
|
||||
AddCategoryMapping(91, TorznabCatType.Movies3D, "FiLMS SD - 3D");
|
||||
AddCategoryMapping(11, TorznabCatType.MoviesDVD, "FiLMS SD - DVD");
|
||||
AddCategoryMapping(53, TorznabCatType.MoviesSD, "FiLMS SD - DVD-SCREENER");
|
||||
AddCategoryMapping(9, TorznabCatType.MoviesDVD, "FiLMS SD - R5");
|
||||
AddCategoryMapping(8, TorznabCatType.MoviesSD, "FiLMS SD - SCREENER");
|
||||
AddCategoryMapping(10, TorznabCatType.MoviesSD, "FiLMS SD - SDRip");
|
||||
AddCategoryMapping(40, TorznabCatType.Movies, "FiLMS SD - VO");
|
||||
AddCategoryMapping(39, TorznabCatType.Movies, "FiLMS SD - VOSTFR");
|
||||
|
||||
AddCategoryMapping(15, TorznabCatType.Console, "JEUX VIDEO");
|
||||
AddCategoryMapping(76, TorznabCatType.Console3DS, "3DS");
|
||||
AddCategoryMapping(18, TorznabCatType.ConsoleNDS, "DS");
|
||||
AddCategoryMapping(55, TorznabCatType.PCPhoneIOS, "IPHONE");
|
||||
AddCategoryMapping(80, TorznabCatType.PCGames, "LINUX");
|
||||
AddCategoryMapping(79, TorznabCatType.PCMac, "OSX");
|
||||
AddCategoryMapping(22, TorznabCatType.PCGames, "PC");
|
||||
AddCategoryMapping(66, TorznabCatType.ConsolePS3, "PS2");
|
||||
AddCategoryMapping(58, TorznabCatType.ConsolePS3, "PS3");
|
||||
AddCategoryMapping(81, TorznabCatType.ConsolePS4, "PS4");
|
||||
AddCategoryMapping(20, TorznabCatType.ConsolePSP, "PSP");
|
||||
AddCategoryMapping(75, TorznabCatType.ConsolePS3, "PSX");
|
||||
AddCategoryMapping(19, TorznabCatType.ConsoleWii, "WII");
|
||||
AddCategoryMapping(83, TorznabCatType.ConsoleWiiU, "WiiU");
|
||||
AddCategoryMapping(16, TorznabCatType.ConsoleXbox, "XBOX");
|
||||
AddCategoryMapping(82, TorznabCatType.ConsoleXboxOne, "XBOX ONE");
|
||||
AddCategoryMapping(17, TorznabCatType.ConsoleXbox360, "XBOX360");
|
||||
AddCategoryMapping(44, TorznabCatType.ConsoleXbox360, "XBOX360.E");
|
||||
AddCategoryMapping(54, TorznabCatType.ConsoleXbox360, "XBOX360.JTAG");
|
||||
AddCategoryMapping(43, TorznabCatType.ConsoleXbox360, "XBOX360.NTSC");
|
||||
AddCategoryMapping(96, TorznabCatType.Console, "NSW");
|
||||
AddCategoryMapping(76, TorznabCatType.Console3DS, "JEUX VIDEO - 3DS");
|
||||
AddCategoryMapping(18, TorznabCatType.ConsoleNDS, "JEUX VIDEO - DS");
|
||||
AddCategoryMapping(55, TorznabCatType.PCPhoneIOS, "JEUX VIDEO - IPHONE");
|
||||
AddCategoryMapping(80, TorznabCatType.PCGames, "JEUX VIDEO - LINUX");
|
||||
AddCategoryMapping(96, TorznabCatType.ConsoleOther, "JEUX VIDEO - NSW");
|
||||
AddCategoryMapping(79, TorznabCatType.PCMac, "JEUX VIDEO - OSX");
|
||||
AddCategoryMapping(22, TorznabCatType.PCGames, "JEUX VIDEO - PC");
|
||||
AddCategoryMapping(66, TorznabCatType.ConsolePS3, "JEUX VIDEO - PS2");
|
||||
AddCategoryMapping(58, TorznabCatType.ConsolePS3, "JEUX VIDEO - PS3");
|
||||
AddCategoryMapping(81, TorznabCatType.ConsolePS4, "JEUX VIDEO - PS4");
|
||||
AddCategoryMapping(20, TorznabCatType.ConsolePSP, "JEUX VIDEO - PSP");
|
||||
AddCategoryMapping(75, TorznabCatType.ConsolePS3, "JEUX VIDEO - PSX");
|
||||
AddCategoryMapping(19, TorznabCatType.ConsoleWii, "JEUX VIDEO - WII");
|
||||
AddCategoryMapping(83, TorznabCatType.ConsoleWiiU, "JEUX VIDEO - WiiU");
|
||||
AddCategoryMapping(16, TorznabCatType.ConsoleXbox, "JEUX VIDEO - XBOX");
|
||||
AddCategoryMapping(82, TorznabCatType.ConsoleXboxOne, "JEUX VIDEO - XBOX ONE");
|
||||
AddCategoryMapping(17, TorznabCatType.ConsoleXbox360, "JEUX VIDEO - XBOX360");
|
||||
|
||||
AddCategoryMapping(23, TorznabCatType.Audio, "MUSIQUES");
|
||||
AddCategoryMapping(26, TorznabCatType.Audio, "CLIP/CONCERT");
|
||||
AddCategoryMapping(61, TorznabCatType.AudioLossless, "FLAC");
|
||||
AddCategoryMapping(60, TorznabCatType.AudioMP3, "MP3");
|
||||
AddCategoryMapping(26, TorznabCatType.Audio, "MUSIQUES - CLIP/CONCERT");
|
||||
AddCategoryMapping(61, TorznabCatType.AudioLossless, "MUSIQUES - FLAC");
|
||||
AddCategoryMapping(60, TorznabCatType.AudioMP3, "MUSIQUES - MP3");
|
||||
|
||||
AddCategoryMapping(30, TorznabCatType.TV, "SERIES");
|
||||
AddCategoryMapping(73, TorznabCatType.TVSD, "Series Pack FR SD");
|
||||
AddCategoryMapping(92, TorznabCatType.TVHD, "Series Pack FR HD");
|
||||
AddCategoryMapping(93, TorznabCatType.TVSD, "Series Pack VOSTFR SD");
|
||||
AddCategoryMapping(94, TorznabCatType.TVHD, "Series Pack VOSTFR HD");
|
||||
AddCategoryMapping(31, TorznabCatType.TVSD, "Series FR SD");
|
||||
AddCategoryMapping(32, TorznabCatType.TVSD, "Series VO SD");
|
||||
AddCategoryMapping(33, TorznabCatType.TVSD, "Series VOSTFR SD");
|
||||
AddCategoryMapping(77, TorznabCatType.TVSD, "Series DVD");
|
||||
AddCategoryMapping(67, TorznabCatType.TVHD, "Series.FR HD");
|
||||
AddCategoryMapping(68, TorznabCatType.TVHD, "Series VO HD");
|
||||
AddCategoryMapping(69, TorznabCatType.TVHD, "Series VOSTFR HD");
|
||||
AddCategoryMapping(77, TorznabCatType.TVSD, "SERIES - DVD");
|
||||
AddCategoryMapping(100, TorznabCatType.TVUHD, "SERIES - 4k");
|
||||
AddCategoryMapping(67, TorznabCatType.TVHD, "SERIES - FR HD");
|
||||
AddCategoryMapping(31, TorznabCatType.TVSD, "SERIES - FR SD");
|
||||
AddCategoryMapping(102, TorznabCatType.TVUHD, "SERIES - Pack 4k");
|
||||
AddCategoryMapping(92, TorznabCatType.TVHD, "SERIES - Pack FR HD");
|
||||
AddCategoryMapping(73, TorznabCatType.TVSD, "SERIES - Pack FR SD");
|
||||
AddCategoryMapping(94, TorznabCatType.TVHD, "SERIES - Pack VOSTFR HD");
|
||||
AddCategoryMapping(93, TorznabCatType.TVSD, "SERIES - Pack VOSTFR SD");
|
||||
AddCategoryMapping(68, TorznabCatType.TVHD, "SERIES - VO HD");
|
||||
AddCategoryMapping(32, TorznabCatType.TVSD, "SERIES - VO SD");
|
||||
AddCategoryMapping(101, TorznabCatType.TVUHD, "SERIES - 4k");
|
||||
AddCategoryMapping(69, TorznabCatType.TVHD, "SERIES - VOSTFR HD");
|
||||
AddCategoryMapping(33, TorznabCatType.TVSD, "SERIES - VOSTFR SD");
|
||||
|
||||
AddCategoryMapping(47, TorznabCatType.TV, "SPECTACLES/EMISSIONS");
|
||||
AddCategoryMapping(71, TorznabCatType.TV, "Emissions");
|
||||
AddCategoryMapping(72, TorznabCatType.TV, "Spectacles");
|
||||
AddCategoryMapping(71, TorznabCatType.TV, "SPECTACLES/EMISSIONS - Emissions");
|
||||
AddCategoryMapping(72, TorznabCatType.TV, "SPECTACLES/EMISSIONS - Spectacles");
|
||||
|
||||
AddCategoryMapping(35, TorznabCatType.TVSport, "SPORT");
|
||||
AddCategoryMapping(36, TorznabCatType.TVSport, "CATCH");
|
||||
AddCategoryMapping(65, TorznabCatType.TVSport, "UFC");
|
||||
AddCategoryMapping(36, TorznabCatType.TVSport, "SPORT - CATCH");
|
||||
AddCategoryMapping(65, TorznabCatType.TVSport, "SPORT - UFC");
|
||||
|
||||
AddCategoryMapping(37, TorznabCatType.XXX, "XXX");
|
||||
}
|
||||
@@ -164,126 +161,116 @@ namespace Jackett.Common.Indexers
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchString = query.GetQueryString();
|
||||
|
||||
var queryCollection = new Dictionary<string, string>();
|
||||
queryCollection.Add("search_type", "t_name");
|
||||
queryCollection.Add("do", "search");
|
||||
queryCollection.Add("keywords", searchString);
|
||||
queryCollection.Add("category", "0"); // multi cat search not supported
|
||||
var pairs = new Dictionary<string, string>
|
||||
{
|
||||
{"do", "search"},
|
||||
{"search_type", !string.IsNullOrWhiteSpace(query.ImdbID) ? "t_genre" : "t_name"},
|
||||
{"keywords", !string.IsNullOrWhiteSpace(query.ImdbID) ? query.ImdbID : query.GetQueryString()},
|
||||
{"category", "0"} // multi cat search not supported
|
||||
};
|
||||
|
||||
var results = await PostDataWithCookies(BrowseUrl, queryCollection);
|
||||
var results = await PostDataWithCookies(BrowseUrl, pairs);
|
||||
if (results.IsRedirect)
|
||||
{
|
||||
// re-login
|
||||
await ApplyConfiguration(null);
|
||||
results = await PostDataWithCookies(BrowseUrl, queryCollection);
|
||||
results = await PostDataWithCookies(BrowseUrl, pairs);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var RowsSelector = "table[id='sortabletable'] > tbody > tr";
|
||||
var SearchResultParser = new HtmlParser();
|
||||
var SearchResultDocument = SearchResultParser.ParseDocument(results.Content);
|
||||
var Rows = SearchResultDocument.QuerySelectorAll(RowsSelector);
|
||||
var lastDate = DateTime.Now;
|
||||
|
||||
foreach (var Row in Rows.Skip(1))
|
||||
var parser = new HtmlParser();
|
||||
var doc = parser.ParseDocument(results.Content);
|
||||
var rows = doc.QuerySelectorAll("table[id='sortabletable'] > tbody > tr");
|
||||
|
||||
foreach (var row in rows.Skip(1))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 0;
|
||||
if (row.Children.Length != 9)
|
||||
continue; // not a torrent line
|
||||
|
||||
var category = Row.QuerySelector("td:nth-child(1) > a");
|
||||
var title = Row.QuerySelector("td:nth-child(2) a");
|
||||
var added = Row.QuerySelector("td:nth-child(2) > div:has(span[style=\"float: right;\"])");
|
||||
if (added == null) // not a torrent line
|
||||
continue;
|
||||
var pretime = added.QuerySelector("font.mkprettytime");
|
||||
var tooltip = Row.QuerySelector("td:nth-child(2) > div.tooltip-content");
|
||||
var cat = row.Children[0].QuerySelector("a").GetAttribute("href").Split('=')[1];
|
||||
var title = row.Children[1].QuerySelector("a").TextContent;
|
||||
var qLinks = row.Children[2].QuerySelectorAll("a");
|
||||
var link = configData.TorrentHTTPSMode.Value ? qLinks[1].GetAttribute("href") : qLinks[0].GetAttribute("href");
|
||||
var comments = row.Children[1].QuerySelector("a").GetAttribute("href");
|
||||
var size = row.Children[4].TextContent;
|
||||
var grabs = row.Children[5].QuerySelector("a").TextContent;
|
||||
var seeders = row.Children[6].QuerySelector("a").TextContent;
|
||||
var leechers = row.Children[7].QuerySelector("a").TextContent;
|
||||
|
||||
var link = Row.QuerySelector("td:nth-child(3)").QuerySelector("a");
|
||||
var comments = Row.QuerySelector("td:nth-child(2)").QuerySelector("a");
|
||||
var Size = Row.QuerySelector("td:nth-child(5)");
|
||||
var Grabs = Row.QuerySelector("td:nth-child(6)").QuerySelector("a");
|
||||
var Seeders = Row.QuerySelector("td:nth-child(7)").QuerySelector("a");
|
||||
var Leechers = Row.QuerySelector("td:nth-child(8)").QuerySelector("a");
|
||||
var qTags = row.Children[1].QuerySelector("div:has(span[style=\"float: right;\"])");
|
||||
var dlVolumeFactor = 1.0;
|
||||
if (qTags.QuerySelector("img[alt^=\"TORRENT GRATUIT\"]") != null)
|
||||
dlVolumeFactor = 0.0;
|
||||
else if (qTags.QuerySelector("img[alt^=\"TORRENT SILVER\"]") != null)
|
||||
dlVolumeFactor = 0.5;
|
||||
|
||||
var categoryId = category.GetAttribute("href").Split('=')[1];
|
||||
release.Category = MapTrackerCatToNewznab(categoryId);
|
||||
var upVolumeFactor = qTags.QuerySelector("img[alt^=\"TORRENT X2\"]") != null ? 2.0 : 1.0;
|
||||
|
||||
release.Title = title.TextContent;
|
||||
release.Category = MapTrackerCatToNewznab(categoryId);
|
||||
release.Link = new Uri(link.GetAttribute("href"));
|
||||
release.Comments = new Uri(comments.GetAttribute("href"));
|
||||
var release = new ReleaseInfo
|
||||
{
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 0,
|
||||
Category = MapTrackerCatToNewznab(cat),
|
||||
Title = title,
|
||||
Link = new Uri(link),
|
||||
Comments = new Uri(comments),
|
||||
Size = ReleaseInfo.GetBytes(size),
|
||||
Seeders = ParseUtil.CoerceInt(seeders),
|
||||
Grabs = ParseUtil.CoerceLong(grabs),
|
||||
DownloadVolumeFactor = dlVolumeFactor,
|
||||
UploadVolumeFactor = upVolumeFactor
|
||||
};
|
||||
release.Peers = ParseUtil.CoerceInt(leechers) + release.Seeders;
|
||||
release.Guid = release.Link;
|
||||
release.Size = ReleaseInfo.GetBytes(Size.TextContent);
|
||||
release.Seeders = ParseUtil.CoerceInt(Seeders.TextContent);
|
||||
release.Peers = ParseUtil.CoerceInt(Leechers.TextContent) + release.Seeders;
|
||||
release.Grabs = ParseUtil.CoerceLong(Grabs.TextContent);
|
||||
|
||||
if (TorrentHTTPSMode)
|
||||
var qTooltip = row.Children[1].QuerySelector("div.tooltip-content");
|
||||
if (qTooltip != null)
|
||||
{
|
||||
var linkHttps = Row.QuerySelector("td:nth-child(4)").QuerySelector("a").GetAttribute("href");
|
||||
var idTorrent = ParseUtil.GetArgumentFromQueryString(linkHttps, "id");
|
||||
release.Link = new Uri($"{SiteLink}download.php?id={idTorrent}&type=ssl");
|
||||
}
|
||||
|
||||
if (added.QuerySelector("img[alt^=\"TORRENT GRATUIT\"]") != null)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else if (added.QuerySelector("img[alt^=\"TORRENT SILVER\"]") != null)
|
||||
release.DownloadVolumeFactor = 0.5;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
if (added.QuerySelector("img[alt^=\"TORRENT X2\"]") != null)
|
||||
release.UploadVolumeFactor = 2;
|
||||
else
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
if (tooltip != null)
|
||||
{
|
||||
var banner = tooltip.QuerySelector("img");
|
||||
var banner = qTooltip.QuerySelector("img");
|
||||
if (banner != null)
|
||||
{
|
||||
release.BannerUrl = new Uri(banner.GetAttribute("src"));
|
||||
banner.Remove();
|
||||
}
|
||||
|
||||
tooltip.QuerySelector("div:contains(\"Total Hits\")").Remove();
|
||||
qTooltip.QuerySelector("div:contains(\"Total Hits\")").Remove();
|
||||
|
||||
var longtitle = tooltip.QuerySelectorAll("div").First();
|
||||
release.Title = longtitle.TextContent;
|
||||
longtitle.Remove();
|
||||
var qLongTitle = qTooltip.QuerySelector("div");
|
||||
release.Title = qLongTitle.TextContent;
|
||||
qLongTitle.Remove();
|
||||
|
||||
var desc = tooltip.TextContent.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(desc))
|
||||
release.Description = desc;
|
||||
var description = qTooltip.TextContent.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(description))
|
||||
release.Description = description;
|
||||
}
|
||||
|
||||
//issue #5064 replace multi keyword
|
||||
if (!string.IsNullOrEmpty(ReplaceMulti))
|
||||
// issue #5064 replace multi keyword
|
||||
if (!string.IsNullOrEmpty(configData.ReplaceMulti.Value))
|
||||
{
|
||||
var regex = new Regex("(?i)([\\.\\- ])MULTI([\\.\\- ])");
|
||||
release.Title = regex.Replace(release.Title, "$1" + ReplaceMulti + "$2");
|
||||
}
|
||||
// issue #6855 Replace VOSTFR with ENGLISH
|
||||
if (configData.Vostfr.Value)
|
||||
{
|
||||
release.Title = release.Title.Replace("VOSTFR", "ENGLISH");
|
||||
release.Title = regex.Replace(release.Title, "$1" + configData.ReplaceMulti.Value + "$2");
|
||||
}
|
||||
|
||||
if (pretime != null)
|
||||
// issue #6855 Replace VOSTFR with ENGLISH
|
||||
if (configData.Vostfr.Value)
|
||||
release.Title = release.Title.Replace("VOSTFR", "ENGLISH");
|
||||
|
||||
var qPretime = qTags.QuerySelector("font.mkprettytime");
|
||||
if (qPretime != null)
|
||||
{
|
||||
if (release.Description == null)
|
||||
release.Description = pretime.TextContent;
|
||||
release.Description = qPretime.TextContent;
|
||||
else
|
||||
release.Description += "<br>\n" + pretime.TextContent;
|
||||
release.Description += "<br>\n" + qPretime.TextContent;
|
||||
release.PublishDate = lastDate;
|
||||
}
|
||||
else
|
||||
{
|
||||
release.PublishDate = DateTime.ParseExact(added.TextContent.Trim(), "dd-MM-yyyy HH:mm", CultureInfo.InvariantCulture);
|
||||
release.PublishDate = DateTime.ParseExact(qTags.TextContent.Trim(), "dd-MM-yyyy HH:mm", CultureInfo.InvariantCulture);
|
||||
lastDate = release.PublishDate;
|
||||
}
|
||||
|
||||
|
186
src/Jackett.Common/Indexers/EpubLibre.cs
Normal file
186
src/Jackett.Common/Indexers/EpubLibre.cs
Normal file
@@ -0,0 +1,186 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Html.Parser;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using WebClient = Jackett.Common.Utils.Clients.WebClient;
|
||||
|
||||
namespace Jackett.Common.Indexers
|
||||
{
|
||||
// ReSharper disable once UnusedType.Global
|
||||
// ReSharper disable once UnusedMember.Global
|
||||
public class EpubLibre : BaseWebIndexer
|
||||
{
|
||||
private string SearchUrl => SiteLink + "catalogo/index/{0}/nuevo/todos/sin/todos/{1}/ajax";
|
||||
private string SobrecargaUrl => SiteLink + "inicio/sobrecarga";
|
||||
private const int MaxItemsPerPage = 18;
|
||||
private const int MaxSearchPageLimit = 6; // 18 items per page * 6 pages = 108
|
||||
private readonly Dictionary<string, string> _apiHeaders = new Dictionary<string, string>
|
||||
{
|
||||
{"X-Requested-With", "XMLHttpRequest"},
|
||||
};
|
||||
private readonly Dictionary<string, string> _languages = new Dictionary<string, string>
|
||||
{
|
||||
{"1", "español"},
|
||||
{"2", "catalán"},
|
||||
{"3", "euskera"},
|
||||
{"4", "gallego"},
|
||||
{"5", "inglés"},
|
||||
{"6", "francés"},
|
||||
{"7", "alemán"},
|
||||
{"8", "sueco"},
|
||||
{"9", "mandarín"},
|
||||
{"10", "italiano"},
|
||||
{"11", "portugués"},
|
||||
{"12", "esperanto"}
|
||||
};
|
||||
|
||||
public EpubLibre(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps)
|
||||
: base("EpubLibre",
|
||||
description: "Más libros, Más libres",
|
||||
link: "https://epublibre.org/",
|
||||
caps: new TorznabCapabilities(TorznabCatType.BooksEbook),
|
||||
configService: configService,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationData())
|
||||
{
|
||||
Encoding = Encoding.UTF8;
|
||||
Language = "es-es";
|
||||
Type = "public";
|
||||
}
|
||||
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
base.LoadValuesFromJson(configJson);
|
||||
var releases = await PerformQuery(new TorznabQuery());
|
||||
await ConfigureIfOK(string.Empty, releases.Any(), () =>
|
||||
throw new Exception("Could not find any release from this URL"));
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
var searchString = "--";
|
||||
var maxPages = 2; // we scrape only 2 pages for recent torrents
|
||||
if (!string.IsNullOrWhiteSpace(query.GetQueryString()))
|
||||
{
|
||||
searchString = Uri.EscapeUriString(query.GetQueryString());
|
||||
maxPages = MaxSearchPageLimit;
|
||||
}
|
||||
|
||||
var lastPublishDate = DateTime.Now;
|
||||
for (var page = 0; page < maxPages; page++)
|
||||
{
|
||||
var searchUrl = string.Format(SearchUrl, page * MaxItemsPerPage, searchString);
|
||||
var result = await RequestStringWithCookies(searchUrl, null, null, _apiHeaders);
|
||||
|
||||
try
|
||||
{
|
||||
var json = JsonConvert.DeserializeObject<dynamic>(result.Content);
|
||||
var parser = new HtmlParser();
|
||||
var doc = parser.ParseDocument((string)json["contenido"]);
|
||||
|
||||
var rows = doc.QuerySelectorAll("div.span2");
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var title = row.QuerySelector("h2").TextContent + " - " +
|
||||
row.QuerySelector("h1").TextContent;
|
||||
if (!CheckTitleMatchWords(query.GetQueryString(), title))
|
||||
continue; // skip if it doesn't contain all words
|
||||
|
||||
var banner = new Uri(row.QuerySelector("img[id=catalog]").GetAttribute("src"));
|
||||
var qLink = row.QuerySelector("a");
|
||||
var comments = new Uri(qLink.GetAttribute("href"));
|
||||
|
||||
var qTooltip = parser.ParseDocument(qLink.GetAttribute("data-content"));
|
||||
// we get the language from the last class tag => class="pull-right sprite idioma_5"
|
||||
var languageId = qTooltip.QuerySelector("div.pull-right").GetAttribute("class").Split('_')[1];
|
||||
title += $" [{_languages[languageId]}] [epub]";
|
||||
var qDesc = qTooltip.QuerySelectorAll("div.row-fluid > div");
|
||||
var description = $"Rev: {qDesc[0].TextContent} Páginas: {qDesc[1].TextContent} Puntación: {qDesc[2].TextContent} Likes: {qDesc[3].TextContent}";
|
||||
|
||||
// publish date is not available in the torrent list, but we add a relative date so we can sort
|
||||
lastPublishDate = lastPublishDate.AddMinutes(-1);
|
||||
|
||||
var release = new ReleaseInfo
|
||||
{
|
||||
Title = title,
|
||||
Comments = comments,
|
||||
Link = comments,
|
||||
Guid = comments,
|
||||
PublishDate = lastPublishDate,
|
||||
BannerUrl = banner,
|
||||
Description = description,
|
||||
Category = new List<int> {TorznabCatType.BooksEbook.ID},
|
||||
Size = 5242880, // 5 MB
|
||||
Seeders = 1,
|
||||
Peers = 2,
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 172800, // 48 hours
|
||||
DownloadVolumeFactor = 0,
|
||||
UploadVolumeFactor = 1
|
||||
};
|
||||
releases.Add(release);
|
||||
}
|
||||
|
||||
if (rows.Length < MaxItemsPerPage)
|
||||
break; // this is the last page
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(result.Content, ex);
|
||||
}
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
public override async Task<byte[]> Download(Uri link)
|
||||
{
|
||||
var result = await RequestStringWithCookies(link.AbsoluteUri);
|
||||
if (SobrecargaUrl.Equals(result.RedirectingTo))
|
||||
throw new Exception("El servidor se encuentra sobrecargado en estos momentos. / The server is currently overloaded.");
|
||||
try {
|
||||
var parser = new HtmlParser();
|
||||
var doc = parser.ParseDocument(result.Content);
|
||||
var magnetLink = doc.QuerySelector("a[id=en_desc]").GetAttribute("href");
|
||||
return Encoding.UTF8.GetBytes(magnetLink);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(result.Content, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: merge this method with query.MatchQueryStringAND
|
||||
private static bool CheckTitleMatchWords(string queryStr, string title)
|
||||
{
|
||||
// this code split the words, remove words with 2 letters or less, remove accents and lowercase
|
||||
var queryMatches = Regex.Matches(queryStr, @"\b[\w']*\b");
|
||||
var queryWords = from m in queryMatches.Cast<Match>()
|
||||
where !string.IsNullOrEmpty(m.Value) && m.Value.Length > 2
|
||||
select Encoding.UTF8.GetString(Encoding.GetEncoding("ISO-8859-8").GetBytes(m.Value.ToLower()));
|
||||
|
||||
var titleMatches = Regex.Matches(title, @"\b[\w']*\b");
|
||||
var titleWords = from m in titleMatches.Cast<Match>()
|
||||
where !string.IsNullOrEmpty(m.Value) && m.Value.Length > 2
|
||||
select Encoding.UTF8.GetString(Encoding.GetEncoding("ISO-8859-8").GetBytes(m.Value.ToLower()));
|
||||
titleWords = titleWords.ToArray();
|
||||
|
||||
return queryWords.All(word => titleWords.Contains(word));
|
||||
}
|
||||
}
|
||||
}
|
@@ -78,11 +78,12 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
var cats = MapTorznabCapsToTrackers(query);
|
||||
var qc = new NameValueCollection
|
||||
{
|
||||
{"incldead", "1"},
|
||||
{"showspam", "1"},
|
||||
{"cat", MapTorznabCapsToTrackers(query).FirstOrDefault() ?? "0"}
|
||||
{"cat", cats.Count == 1 ? cats[0] : "0"}
|
||||
};
|
||||
if (!string.IsNullOrWhiteSpace(query.GetQueryString()))
|
||||
qc.Add("search", query.GetQueryString());
|
||||
|
@@ -5,7 +5,6 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Dom;
|
||||
using AngleSharp.Html.Parser;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig;
|
||||
@@ -89,7 +88,7 @@ namespace Jackett.Common.Indexers
|
||||
var loginPage = await RequestStringWithCookies(SiteLink, string.Empty);
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, loginPage.Cookies, true, SiteLink, SiteLink);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
await ConfigureIfOK(result.Cookies, result.Content?.Contains("logout.php") == true, () =>
|
||||
{
|
||||
var parser = new HtmlParser();
|
||||
var dom = parser.ParseDocument(result.Content);
|
||||
@@ -105,7 +104,6 @@ namespace Jackett.Common.Indexers
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = BrowseUrl;
|
||||
var trackerCats = MapTorznabCapsToTrackers(query);
|
||||
var queryCollection = new NameValueCollection();
|
||||
|
||||
// Tracker can only search OR return things in categories
|
||||
@@ -117,21 +115,11 @@ namespace Jackett.Common.Indexers
|
||||
else
|
||||
{
|
||||
foreach (var cat in MapTorznabCapsToTrackers(query))
|
||||
{
|
||||
queryCollection.Add("c" + cat, "1");
|
||||
}
|
||||
|
||||
queryCollection.Add("incldead", "0");
|
||||
}
|
||||
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
await ProcessPage(releases, searchUrl);
|
||||
return releases;
|
||||
}
|
||||
|
||||
private async Task ProcessPage(List<ReleaseInfo> releases, string searchUrl)
|
||||
{
|
||||
var response = await RequestStringWithCookiesAndRetry(searchUrl, null, BrowseUrl);
|
||||
if (response.IsRedirect)
|
||||
{
|
||||
@@ -146,7 +134,9 @@ namespace Jackett.Common.Indexers
|
||||
var parser = new HtmlParser();
|
||||
var dom = parser.ParseDocument(results);
|
||||
|
||||
var rows = dom.QuerySelectorAll(".browsetable").Last().QuerySelectorAll("tr"); //the class for the table changed
|
||||
var rows = dom.QuerySelectorAll(".browsetable").LastOrDefault()?.QuerySelectorAll("tr");
|
||||
if (rows == null)
|
||||
return releases;
|
||||
foreach (var row in rows.Skip(1))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
@@ -159,9 +149,7 @@ namespace Jackett.Common.Indexers
|
||||
|
||||
// If we search an get no results, we still get a table just with no info.
|
||||
if (string.IsNullOrWhiteSpace(release.Title))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if the release has been assigned a category
|
||||
var category = row.QuerySelector("td:nth-of-type(1) a");
|
||||
@@ -193,6 +181,8 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Dom;
|
||||
using AngleSharp.Html.Parser;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig;
|
||||
@@ -41,6 +41,8 @@ namespace Jackett.Common.Indexers
|
||||
Encoding = Encoding.UTF8;
|
||||
Language = "en-us";
|
||||
Type = "private";
|
||||
TorznabCaps.SupportsImdbMovieSearch = true;
|
||||
TorznabCaps.SupportsImdbTVSearch = true;
|
||||
|
||||
AddCategoryMapping(15, TorznabCatType.MoviesBluRay); // Movie / Blu-ray
|
||||
AddMultiCategoryMapping(TorznabCatType.MoviesHD,
|
||||
@@ -79,7 +81,7 @@ namespace Jackett.Common.Indexers
|
||||
};
|
||||
|
||||
// Send Post
|
||||
var response = await RequestLoginAndFollowRedirect(LoginUrl, pairs, loginPage.Cookies, true, null, LoginUrl);
|
||||
var response = await RequestLoginAndFollowRedirect(LoginUrl, pairs, loginPage.Cookies, true, referer: LoginUrl);
|
||||
|
||||
await ConfigureIfOK(response.Cookies, response.Content?.Contains("logout.php") == true, () =>
|
||||
{
|
||||
@@ -99,13 +101,19 @@ namespace Jackett.Common.Indexers
|
||||
var queryCollection = new NameValueCollection
|
||||
{
|
||||
{"active", "0"},
|
||||
{"options", "0"},
|
||||
{"category", string.Join(";", MapTorznabCapsToTrackers(query))}
|
||||
};
|
||||
|
||||
var searchString = query.GetQueryString();
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
queryCollection.Add("search", searchString);
|
||||
if (query.IsImdbQuery)
|
||||
{
|
||||
queryCollection.Add("options", "2");
|
||||
queryCollection.Add("search", query.ImdbIDShort);
|
||||
}
|
||||
else
|
||||
{
|
||||
queryCollection.Add("options", "0");
|
||||
queryCollection.Add("search", query.GetQueryString());
|
||||
}
|
||||
|
||||
var response = await RequestStringWithCookiesAndRetry(SearchUrl + queryCollection.GetQueryString());
|
||||
|
||||
@@ -119,7 +127,7 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
// this tracker has horrible markup, find the result rows by looking for the style tag before each one
|
||||
var prev = row.PreviousElementSibling;
|
||||
if (prev == null || prev.NodeName.ToLowerInvariant() != "style")
|
||||
if (prev == null || !string.Equals(prev.NodeName, "style", StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
var release = new ReleaseInfo();
|
||||
@@ -131,12 +139,16 @@ namespace Jackett.Common.Indexers
|
||||
release.Comments = new Uri(SiteLink + qLink.GetAttribute("href"));
|
||||
release.Guid = release.Comments;
|
||||
|
||||
var imdbLink = row.Children[1].QuerySelector("a[href*=imdb]");
|
||||
if(imdbLink != null)
|
||||
release.Imdb = ParseUtil.GetImdbID(imdbLink.GetAttribute("href").Split('/').Last());
|
||||
|
||||
var qDownload = row.Children[3].FirstElementChild;
|
||||
release.Link = new Uri(SiteLink + qDownload.GetAttribute("href"));
|
||||
|
||||
var dateStr = row.Children[4].TextContent.Trim();
|
||||
//"July 11, 2015, 13:34:09", "Today|Yesterday at 20:04:23"
|
||||
release.PublishDate = DateTimeUtil.FromUnknown(dateStr.Replace("at", string.Empty));
|
||||
release.PublishDate = DateTimeUtil.FromUnknown(dateStr);
|
||||
var sizeStr = row.Children[5].TextContent;
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
release.Seeders = ParseUtil.CoerceInt(row.Children[7].TextContent);
|
||||
|
@@ -422,7 +422,7 @@ namespace Jackett.Common.Indexers
|
||||
|
||||
// Building our tracker query
|
||||
parameters.Add("incldead", "1");
|
||||
parameters.Add("fullsearch", "0");
|
||||
parameters.Add("fullsearch", ConfigData.UseFullSearch.Value ? "1" : "0");
|
||||
parameters.Add("scenerelease", "0");
|
||||
|
||||
// If search term provided
|
||||
|
@@ -2,18 +2,19 @@ namespace Jackett.Common.Models.IndexerConfig.Bespoke
|
||||
{
|
||||
internal class ConfigurationDataEliteTracker : ConfigurationDataBasicLogin
|
||||
{
|
||||
public BoolItem TorrentHTTPSMode { get; private set; }
|
||||
public DisplayItem PagesWarning { get; private set; }
|
||||
public StringItem ReplaceMulti { get; private set; }
|
||||
public BoolItem Vostfr { get; private set; }
|
||||
public BoolItem TorrentHTTPSMode { get; }
|
||||
// ReSharper disable once MemberCanBePrivate.Global
|
||||
// ReSharper disable once UnusedAutoPropertyAccessor.Global
|
||||
public DisplayItem PagesWarning { get; }
|
||||
public StringItem ReplaceMulti { get; }
|
||||
public BoolItem Vostfr { get; }
|
||||
|
||||
public ConfigurationDataEliteTracker()
|
||||
: base()
|
||||
{
|
||||
TorrentHTTPSMode = new BoolItem { Name = "Use https for tracker URL", Value = false };
|
||||
TorrentHTTPSMode = new BoolItem { Name = "Use HTTPS for tracker URL", Value = false };
|
||||
PagesWarning = new DisplayItem("<b>Preferences Configuration</b> (<i>Tweak your search settings</i>),<br /><br /> <ul><li><b>Replace MULTI</b>, replace multi keyword in the resultset (leave empty to deactivate)</li><li><b>Replace VOSTFR with ENGLISH</b> lets you change the titles by replacing VOSTFR with ENGLISH.</li></ul>") { Name = "Preferences" };
|
||||
ReplaceMulti = new StringItem() { Name = "Replace MULTI", Value = "MULTI.FRENCH" };
|
||||
Vostfr = new BoolItem() { Name = "Replace VOSTFR with ENGLISH", Value = false };
|
||||
ReplaceMulti = new StringItem { Name = "Replace MULTI", Value = "MULTI.FRENCH" };
|
||||
Vostfr = new BoolItem { Name = "Replace VOSTFR with ENGLISH", Value = false };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ namespace Jackett.Common.Models.IndexerConfig.Bespoke
|
||||
public StringItem Password { get; private set; }
|
||||
public DisplayItem PagesWarning { get; private set; }
|
||||
public StringItem Pages { get; private set; }
|
||||
public BoolItem UseFullSearch { get; private set; }
|
||||
public DisplayItem SecurityWarning { get; private set; }
|
||||
public BoolItem Latency { get; private set; }
|
||||
public BoolItem Browser { get; private set; }
|
||||
@@ -31,6 +32,7 @@ namespace Jackett.Common.Models.IndexerConfig.Bespoke
|
||||
Password = new StringItem { Name = "Password", Value = "" };
|
||||
PagesWarning = new DisplayItem("<b>Preferences Configuration</b> (<i>Tweak your search settings</i>),<br /><br /> <ul><li><b>Max Pages to Process</b> let you specify how many page (max) Jackett can process when doing a search. Setting a value <b>higher than 4 is dangerous</b> for you account ! (<b>Result of too many requests to tracker...that <u>will be suspect</u></b>).</li></ul>") { Name = "Preferences" };
|
||||
Pages = new StringItem { Name = "Max Pages to Process (Required)", Value = "4" };
|
||||
UseFullSearch = new BoolItem { Name = "Enable search in description.", Value = false };
|
||||
SecurityWarning = new DisplayItem("<b>Security Configuration</b> (<i>Read this area carefully !</i>),<br /><br /> <ul><li><b>Latency Simulation</b> will simulate human browsing with Jacket by pausing Jacket for an random time between each request, to fake a real content browsing.</li><li><b>Browser Simulation</b> will simulate a real human browser by injecting additionals headers when doing requests to tracker.<b>You must enable it to use this provider!</b></li></ul>") { Name = "Security" };
|
||||
Latency = new BoolItem() { Name = "Latency Simulation (Optional)", Value = false };
|
||||
Browser = new BoolItem() { Name = "Browser Simulation (Forced)", Value = true };
|
||||
|
@@ -54,12 +54,9 @@ namespace Jackett.Common.Utils.Clients
|
||||
public static void InitProxy(ServerConfig serverConfig)
|
||||
{
|
||||
// dispose old SocksWebProxy
|
||||
if (webProxy != null && webProxy is SocksWebProxy)
|
||||
{
|
||||
((SocksWebProxy)webProxy).Dispose();
|
||||
webProxy = null;
|
||||
}
|
||||
|
||||
if (webProxy is SocksWebProxy proxy)
|
||||
proxy.Dispose();
|
||||
webProxy = null;
|
||||
webProxyUrl = serverConfig.GetProxyUrl();
|
||||
if (!string.IsNullOrWhiteSpace(webProxyUrl))
|
||||
{
|
||||
|
@@ -61,12 +61,9 @@ namespace Jackett.Common.Utils.Clients
|
||||
public static void InitProxy(ServerConfig serverConfig)
|
||||
{
|
||||
// dispose old SocksWebProxy
|
||||
if (webProxy != null && webProxy is SocksWebProxy)
|
||||
{
|
||||
((SocksWebProxy)webProxy).Dispose();
|
||||
webProxy = null;
|
||||
}
|
||||
|
||||
if (webProxy is SocksWebProxy proxy)
|
||||
proxy.Dispose();
|
||||
webProxy = null;
|
||||
webProxyUrl = serverConfig.GetProxyUrl();
|
||||
if (!string.IsNullOrWhiteSpace(webProxyUrl))
|
||||
{
|
||||
|
@@ -57,12 +57,9 @@ namespace Jackett.Common.Utils.Clients
|
||||
public static void InitProxy(ServerConfig serverConfig)
|
||||
{
|
||||
// dispose old SocksWebProxy
|
||||
if (webProxy != null && webProxy is SocksWebProxy)
|
||||
{
|
||||
((SocksWebProxy)webProxy).Dispose();
|
||||
webProxy = null;
|
||||
}
|
||||
|
||||
if (webProxy is SocksWebProxy proxy)
|
||||
proxy.Dispose();
|
||||
webProxy = null;
|
||||
webProxyUrl = serverConfig.GetProxyUrl();
|
||||
if (!string.IsNullOrWhiteSpace(webProxyUrl))
|
||||
{
|
||||
|
@@ -53,12 +53,9 @@ namespace Jackett.Common.Utils.Clients
|
||||
public static void InitProxy(ServerConfig serverConfig)
|
||||
{
|
||||
// dispose old SocksWebProxy
|
||||
if (webProxy != null && webProxy is SocksWebProxy)
|
||||
{
|
||||
((SocksWebProxy)webProxy).Dispose();
|
||||
webProxy = null;
|
||||
}
|
||||
|
||||
if (webProxy is SocksWebProxy proxy)
|
||||
proxy.Dispose();
|
||||
webProxy = null;
|
||||
webProxyUrl = serverConfig.GetProxyUrl();
|
||||
if (!string.IsNullOrWhiteSpace(webProxyUrl))
|
||||
{
|
||||
|
@@ -6,18 +6,26 @@ namespace Jackett.Common.Utils
|
||||
{
|
||||
public static class DateTimeUtil
|
||||
{
|
||||
public static string RFC1123ZPattern = "ddd, dd MMM yyyy HH':'mm':'ss z";
|
||||
public const string Rfc1123ZPattern = "ddd, dd MMM yyyy HH':'mm':'ss z";
|
||||
|
||||
private static readonly Regex _TimeAgoRegexp = new Regex(@"(?i)\bago", RegexOptions.Compiled);
|
||||
private static readonly Regex _TodayRegexp = new Regex(@"(?i)\btoday(?:[\s,]+(?:at){0,1}\s*|[\s,]*|$)", RegexOptions.Compiled);
|
||||
private static readonly Regex _TomorrowRegexp = new Regex(@"(?i)\btomorrow(?:[\s,]+(?:at){0,1}\s*|[\s,]*|$)", RegexOptions.Compiled);
|
||||
private static readonly Regex _YesterdayRegexp = new Regex(@"(?i)\byesterday(?:[\s,]+(?:at){0,1}\s*|[\s,]*|$)", RegexOptions.Compiled);
|
||||
private static readonly Regex _DaysOfWeekRegexp = new Regex(@"(?i)\b(monday|tuesday|wednesday|thursday|friday|saturday|sunday)\s+at\s+", RegexOptions.Compiled);
|
||||
private static readonly Regex _MissingYearRegexp = new Regex(@"^(\d{1,2}-\d{1,2})(\s|$)", RegexOptions.Compiled);
|
||||
private static readonly Regex _MissingYearRegexp2 = new Regex(@"^(\d{1,2}\s+\w{3})\s+(\d{1,2}\:\d{1,2}.*)$", RegexOptions.Compiled); // 1 Jan 10:30
|
||||
|
||||
public static DateTime UnixTimestampToDateTime(long unixTime)
|
||||
{
|
||||
var dt = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
|
||||
var dt = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||
dt = dt.AddSeconds(unixTime).ToLocalTime();
|
||||
return dt;
|
||||
}
|
||||
|
||||
public static DateTime UnixTimestampToDateTime(double unixTime)
|
||||
{
|
||||
var unixStart = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
|
||||
var unixStart = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||
var unixTimeStampInTicks = (long)(unixTime * TimeSpan.TicksPerSecond);
|
||||
return new DateTime(unixStart.Ticks + unixTimeStampInTicks);
|
||||
}
|
||||
@@ -35,25 +43,21 @@ namespace Jackett.Common.Utils
|
||||
{
|
||||
str = str.ToLowerInvariant();
|
||||
if (str.Contains("now"))
|
||||
{
|
||||
return DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local);
|
||||
}
|
||||
|
||||
str = str.Replace(",", "");
|
||||
str = str.Replace("ago", "");
|
||||
str = str.Replace("and", "");
|
||||
|
||||
var timeAgo = TimeSpan.Zero;
|
||||
var TimeagoRegex = new Regex(@"\s*?([\d\.]+)\s*?([^\d\s\.]+)\s*?");
|
||||
var TimeagoMatches = TimeagoRegex.Match(str);
|
||||
var timeagoRegex = new Regex(@"\s*?([\d\.]+)\s*?([^\d\s\.]+)\s*?");
|
||||
var timeagoMatches = timeagoRegex.Match(str);
|
||||
|
||||
while (TimeagoMatches.Success)
|
||||
while (timeagoMatches.Success)
|
||||
{
|
||||
var expanded = string.Empty;
|
||||
|
||||
var val = ParseUtil.CoerceFloat(TimeagoMatches.Groups[1].Value);
|
||||
var unit = TimeagoMatches.Groups[2].Value;
|
||||
TimeagoMatches = TimeagoMatches.NextMatch();
|
||||
var val = ParseUtil.CoerceFloat(timeagoMatches.Groups[1].Value);
|
||||
var unit = timeagoMatches.Groups[2].Value;
|
||||
timeagoMatches = timeagoMatches.NextMatch();
|
||||
|
||||
if (unit.Contains("sec") || unit == "s")
|
||||
timeAgo += TimeSpan.FromSeconds(val);
|
||||
@@ -70,77 +74,37 @@ namespace Jackett.Common.Utils
|
||||
else if (unit.Contains("year") || unit == "y")
|
||||
timeAgo += TimeSpan.FromDays(val * 365);
|
||||
else
|
||||
{
|
||||
throw new Exception("TimeAgo parsing failed, unknown unit: " + unit);
|
||||
}
|
||||
}
|
||||
|
||||
return DateTime.SpecifyKind(DateTime.Now - timeAgo, DateTimeKind.Local);
|
||||
}
|
||||
|
||||
public static TimeSpan ParseTimeSpan(string time)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(time))
|
||||
return TimeSpan.Zero;
|
||||
|
||||
var offset = TimeSpan.Zero;
|
||||
if (time.EndsWith("AM"))
|
||||
{
|
||||
time = time.Substring(0, time.Length - 2);
|
||||
if (time.StartsWith("12")) // 12:15 AM becomes 00:15
|
||||
time = "00" + time.Substring(2);
|
||||
}
|
||||
else if (time.EndsWith("PM"))
|
||||
{
|
||||
time = time.Substring(0, time.Length - 2);
|
||||
offset = TimeSpan.FromHours(12);
|
||||
}
|
||||
|
||||
var ts = TimeSpan.Parse(time);
|
||||
ts += offset;
|
||||
return ts;
|
||||
}
|
||||
|
||||
// Uses the DateTimeRoutines library to parse the date
|
||||
// http://www.codeproject.com/Articles/33298/C-Date-Time-Parser
|
||||
public static DateTime FromFuzzyTime(string str, string format = null)
|
||||
{
|
||||
var dt_format = DateTimeRoutines.DateTimeRoutines.DateTimeFormat.USA_DATE;
|
||||
if (format == "UK")
|
||||
{
|
||||
dt_format = DateTimeRoutines.DateTimeRoutines.DateTimeFormat.UK_DATE;
|
||||
}
|
||||
var dtFormat = format == "UK" ?
|
||||
DateTimeRoutines.DateTimeRoutines.DateTimeFormat.UkDate :
|
||||
DateTimeRoutines.DateTimeRoutines.DateTimeFormat.UsaDate;
|
||||
|
||||
if (DateTimeRoutines.DateTimeRoutines.TryParseDateOrTime(str, dt_format, out
|
||||
DateTimeRoutines.DateTimeRoutines.ParsedDateTime dt))
|
||||
{
|
||||
if (DateTimeRoutines.DateTimeRoutines.TryParseDateOrTime(
|
||||
str, dtFormat, out DateTimeRoutines.DateTimeRoutines.ParsedDateTime dt))
|
||||
return dt.DateTime;
|
||||
}
|
||||
|
||||
throw new Exception("FromFuzzyTime parsing failed");
|
||||
}
|
||||
|
||||
public static Regex timeAgoRegexp = new Regex(@"(?i)\bago", RegexOptions.Compiled);
|
||||
public static Regex todayRegexp = new Regex(@"(?i)\btoday([\s,]*|$)", RegexOptions.Compiled);
|
||||
public static Regex tomorrowRegexp = new Regex(@"(?i)\btomorrow([\s,]*|$)", RegexOptions.Compiled);
|
||||
public static Regex yesterdayRegexp = new Regex(@"(?i)\byesterday([\s,]*|$)", RegexOptions.Compiled);
|
||||
public static Regex daysOfWeekRegexp = new Regex(@"(?i)\b(monday|tuesday|wednesday|thursday|friday|saturday|sunday)\s+at\s+", RegexOptions.Compiled);
|
||||
public static Regex missingYearRegexp = new Regex(@"^(\d{1,2}-\d{1,2})(\s|$)", RegexOptions.Compiled);
|
||||
public static Regex missingYearRegexp2 = new Regex(@"^(\d{1,2}\s+\w{3})\s+(\d{1,2}\:\d{1,2}.*)$", RegexOptions.Compiled); // 1 Jan 10:30
|
||||
|
||||
public static DateTime FromUnknown(string str, string format = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
str = ParseUtil.NormalizeSpace(str);
|
||||
Match match;
|
||||
|
||||
if (str.ToLower().Contains("now"))
|
||||
{
|
||||
return DateTime.UtcNow;
|
||||
}
|
||||
|
||||
// ... ago
|
||||
match = timeAgoRegexp.Match(str);
|
||||
var match = _TimeAgoRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var timeago = str;
|
||||
@@ -148,7 +112,7 @@ namespace Jackett.Common.Utils
|
||||
}
|
||||
|
||||
// Today ...
|
||||
match = todayRegexp.Match(str);
|
||||
match = _TodayRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var time = str.Replace(match.Groups[0].Value, "");
|
||||
@@ -158,7 +122,7 @@ namespace Jackett.Common.Utils
|
||||
}
|
||||
|
||||
// Yesterday ...
|
||||
match = yesterdayRegexp.Match(str);
|
||||
match = _YesterdayRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var time = str.Replace(match.Groups[0].Value, "");
|
||||
@@ -169,7 +133,7 @@ namespace Jackett.Common.Utils
|
||||
}
|
||||
|
||||
// Tomorrow ...
|
||||
match = tomorrowRegexp.Match(str);
|
||||
match = _TomorrowRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var time = str.Replace(match.Groups[0].Value, "");
|
||||
@@ -180,14 +144,14 @@ namespace Jackett.Common.Utils
|
||||
}
|
||||
|
||||
// [day of the week] at ... (eg: Saturday at 14:22)
|
||||
match = daysOfWeekRegexp.Match(str);
|
||||
match = _DaysOfWeekRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var time = str.Replace(match.Groups[0].Value, "");
|
||||
var dt = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified);
|
||||
dt += ParseTimeSpan(time);
|
||||
|
||||
var dow = DayOfWeek.Monday;
|
||||
DayOfWeek dow;
|
||||
var groupMatchLower = match.Groups[1].Value.ToLower();
|
||||
if (groupMatchLower.StartsWith("monday"))
|
||||
dow = DayOfWeek.Monday;
|
||||
@@ -221,28 +185,28 @@ namespace Jackett.Common.Utils
|
||||
}
|
||||
|
||||
// add missing year
|
||||
match = missingYearRegexp.Match(str);
|
||||
match = _MissingYearRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var date = match.Groups[1].Value;
|
||||
var newDate = DateTime.Now.Year.ToString() + "-" + date;
|
||||
var newDate = DateTime.Now.Year + "-" + date;
|
||||
str = str.Replace(date, newDate);
|
||||
}
|
||||
|
||||
// add missing year 2
|
||||
match = missingYearRegexp2.Match(str);
|
||||
match = _MissingYearRegexp2.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var date = match.Groups[1].Value;
|
||||
var time = match.Groups[2].Value;
|
||||
str = date + " " + DateTime.Now.Year.ToString() + " " + time;
|
||||
str = date + " " + DateTime.Now.Year + " " + time;
|
||||
}
|
||||
|
||||
return FromFuzzyTime(str, format);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception(string.Format("DateTime parsing failed for \"{0}\": {1}", str, ex.ToString()));
|
||||
throw new Exception($"DateTime parsing failed for \"{str}\": {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,8 +283,13 @@ namespace Jackett.Common.Utils
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
throw new FormatException(string.Format("Error while parsing DateTime \"{0}\", using layout \"{1}\" ({2}): {3}", date, layout, pattern, ex.Message));
|
||||
throw new FormatException($"Error while parsing DateTime \"{date}\", using layout \"{layout}\" ({pattern}): {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static TimeSpan ParseTimeSpan(string time) =>
|
||||
string.IsNullOrWhiteSpace(time)
|
||||
? TimeSpan.Zero
|
||||
: DateTime.Parse(time).TimeOfDay;
|
||||
}
|
||||
}
|
||||
|
31
src/Jackett.Test/Utils/DateTimeUtilTests.cs
Normal file
31
src/Jackett.Test/Utils/DateTimeUtilTests.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Jackett.Common.Utils.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class DateTimeUtilTests
|
||||
{
|
||||
[Test]
|
||||
public void FromUnknownTest()
|
||||
{
|
||||
var today = DateTime.UtcNow.Date;
|
||||
var yesterday = today.AddDays(-1);
|
||||
var tomorrow = today.AddDays(1);
|
||||
var testCases = new Dictionary<string, DateTime>
|
||||
{
|
||||
{"today, at 1:00 PM", today.AddHours(13)},
|
||||
{"today at 12:00PM", today.AddHours(12)},
|
||||
{"Today", today},
|
||||
{"Today at 20:19:54", today.AddHours(20).AddMinutes(19).AddSeconds(54)},
|
||||
{"Today 22:29", today.AddHours(22).AddMinutes(29)},
|
||||
{"Yesterday\n 19:54:54", yesterday.AddHours(19).AddMinutes(54).AddSeconds(54)},
|
||||
{"Yesterday\n 11:55 PM", yesterday.AddHours(23).AddMinutes(55)}
|
||||
};
|
||||
|
||||
foreach (var testCase in testCases)
|
||||
Assert.AreEqual( testCase.Value, DateTimeUtil.FromUnknown(testCase.Key));
|
||||
}
|
||||
}
|
||||
}
|
@@ -372,6 +372,7 @@ namespace Jackett.Updater
|
||||
"Definitions/torrentseeds.yml", // migrated to c#
|
||||
"Definitions/torrentsmd.yml",
|
||||
"Definitions/torrentvault.yml",
|
||||
"Definitions/torrentwal.yml",
|
||||
"Definitions/torrentwtf.yml",
|
||||
"Definitions/torrof.yml",
|
||||
"Definitions/torviet.yml",
|
||||
|
Reference in New Issue
Block a user