mirror of
https://github.com/Jackett/Jackett.git
synced 2025-12-15 11:45:04 +01:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0dbd475dc | ||
|
|
14b132e4f3 | ||
|
|
5e78223cdc | ||
|
|
4cd883d432 | ||
|
|
a1a98f9836 | ||
|
|
0c9c144e5a | ||
|
|
a2da916252 | ||
|
|
8d85708721 | ||
|
|
a00ff4703e | ||
|
|
fcf7694ae7 | ||
|
|
503030045f | ||
|
|
5f53bea74a | ||
|
|
855d89296a | ||
|
|
7800813aee | ||
|
|
2151dba5d1 | ||
|
|
8e1887a133 | ||
|
|
587bef69d4 | ||
|
|
32863c2aa9 | ||
|
|
6e0a3ea759 | ||
|
|
7de44d8c9e | ||
|
|
53646c28de | ||
|
|
aa0ae46fd9 | ||
|
|
535f199e84 | ||
|
|
64dd9c6897 | ||
|
|
eb3898a961 | ||
|
|
75bf8d7078 | ||
|
|
390b1714f4 | ||
|
|
6254d62c23 | ||
|
|
08773ae3c9 | ||
|
|
7b7f77e06f | ||
|
|
64fc61f777 | ||
|
|
dee082ead3 | ||
|
|
6347fdff66 | ||
|
|
cdeac3c0ef | ||
|
|
6d568a4059 | ||
|
|
3dd5b1e5f0 | ||
|
|
fb9845d1e3 | ||
|
|
70b0aa1f67 | ||
|
|
368afcfc5a | ||
|
|
29d28e2607 | ||
|
|
b023e8fe6d | ||
|
|
d51752f682 | ||
|
|
ec40699cdc | ||
|
|
3c798127c5 | ||
|
|
61b263dd98 | ||
|
|
dd11b7e6cd | ||
|
|
bec42c4ac0 | ||
|
|
9b08d7ad46 | ||
|
|
9311af24b5 | ||
|
|
9e5d79d2a4 |
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -57,7 +57,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
8
.github/workflows/definitions-update.yml
vendored
8
.github/workflows/definitions-update.yml
vendored
@@ -15,7 +15,7 @@ concurrency:
|
||||
jobs:
|
||||
update-rudub:
|
||||
name: Update rudub
|
||||
if: github.repository == 'Jackett/Jackett'
|
||||
if: (github.repository == 'Jackett/Jackett' && github.actor != 'dependabot[bot]')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -34,14 +34,14 @@ jobs:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
- name: Setup Environment Variables
|
||||
run: |
|
||||
echo "CURRENT_RUDUB_DOMAIN=$(date +'%b%d' | tr '[:upper:]' '[:lower:]').rudub.pics" >> $GITHUB_ENV
|
||||
echo "PREVIOUS_RUDUB_DOMAIN=$(date --date='yesterday' +'%b%d' | tr '[:upper:]' '[:lower:]').rudub.pics" >> $GITHUB_ENV
|
||||
echo "CURRENT_RUDUB_DOMAIN=$(date +'%b%d' | tr '[:upper:]' '[:lower:]').rudub.world" >> $GITHUB_ENV
|
||||
echo "PREVIOUS_RUDUB_DOMAIN=$(date --date='yesterday' +'%b%d' | tr '[:upper:]' '[:lower:]').rudub.world" >> $GITHUB_ENV
|
||||
|
||||
- name: Update rudub definition
|
||||
uses: mikefarah/yq@master
|
||||
|
||||
2
.github/workflows/issuebot.yml
vendored
2
.github/workflows/issuebot.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
steps:
|
||||
# This workflow will run a script that is stored in the repository. This step checks out the repository contents so that the workflow can access the script.
|
||||
- name: Check out repo content
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
# This step sets up Node.js. The script that this workflow will run uses Node.js.
|
||||
- name: Setup Node.js
|
||||
|
||||
@@ -182,4 +182,4 @@ search:
|
||||
description:
|
||||
selector: td.rowfollow:nth-child(2)
|
||||
remove: a, b, font, img, span
|
||||
# NexusPHP v1.9.10 2025-10-30
|
||||
# NexusPHP v1.9.11 2025-11-02
|
||||
|
||||
@@ -94,8 +94,6 @@ search:
|
||||
selector: details_link
|
||||
download:
|
||||
selector: download_link
|
||||
infohash:
|
||||
selector: info_hash
|
||||
files:
|
||||
selector: num_file
|
||||
seeders:
|
||||
@@ -145,4 +143,4 @@ search:
|
||||
minimumseedtime:
|
||||
# 7 days (as seconds = 7 x 24 x 60 x 60)
|
||||
text: 604800
|
||||
# json UNIT3D 9.1.5 (custom)
|
||||
# json UNIT3D 9.1.7 (custom)
|
||||
|
||||
@@ -5,6 +5,8 @@ description: "ExtremeBits is a Private Torrent Tracker for EXTREME SPORTS"
|
||||
language: en-US
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
certificates:
|
||||
- 0e7f0e9c468938a43058d72c69ffdb9a98e24eab # Expired 26th Nov 2025
|
||||
links:
|
||||
- https://extremebits.net/
|
||||
|
||||
|
||||
@@ -231,4 +231,4 @@ search:
|
||||
filters:
|
||||
- name: prepend
|
||||
args: "{{ .Result.description_verified }} "
|
||||
# NexusPHP v1.9.10 2025-10-30
|
||||
# NexusPHP v1.9.11 2025-11-02
|
||||
|
||||
@@ -5,8 +5,6 @@ description: "HDGalaKtik is a RUSSIAN Semi-Private tracker for MOVIES / TV / GEN
|
||||
language: ru-RU
|
||||
type: semi-private
|
||||
encoding: UTF-8
|
||||
certificates:
|
||||
- 89cb539248b0d0cb0e92aa3f286ddfdd8347c3be # CN=mail.trackerpmr.com
|
||||
links:
|
||||
- https://www.trackerpmr.com/
|
||||
- https://freetmd.com/
|
||||
@@ -16,20 +14,20 @@ legacylinks:
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 13, cat: Movies, desc: "Фильмы (Movies)"}
|
||||
- {id: 6, cat: TV, desc: "Мультфильмы (Cartoons)"}
|
||||
- {id: 10, cat: Audio, desc: "Музыка (Music)"}
|
||||
- {id: 26, cat: PC, desc: "Программы (Programs)"}
|
||||
- {id: 5, cat: Console, desc: "Игры (Games)"}
|
||||
- {id: 25, cat: Other, desc: "Картинки (Pictures)"}
|
||||
- {id: 11, cat: TV, desc: "Сериалы (TV Series)"}
|
||||
- {id: 12, cat: TV/Anime, desc: "Аниме (Anime)"}
|
||||
- {id: 16, cat: Books, desc: "Книги (Books)"}
|
||||
- {id: 18, cat: Audio/Video, desc: "Клипы / Ролики (Clips / Trailers)"}
|
||||
- {id: 22, cat: TV, desc: "ТВ / Передачи (TV)"}
|
||||
- {id: 27, cat: PC/Mobile-Other, desc: "Игры - Мобила / КПК (Mobile)"}
|
||||
- {id: 1, cat: PC/ISO, desc: "Образы (ISO)"}
|
||||
- {id: 4, cat: Other, desc: "Другое (Other)"}
|
||||
- {id: 13, cat: Movies, desc: "Фильмы"}
|
||||
- {id: 11, cat: TV, desc: "Сериалы"}
|
||||
- {id: 6, cat: TV, desc: "Мультфильмы"}
|
||||
- {id: 10, cat: Audio, desc: "Музыка"}
|
||||
- {id: 26, cat: PC, desc: "Программы"}
|
||||
- {id: 5, cat: Console, desc: "Игры"}
|
||||
- {id: 25, cat: Other, desc: "Картинки"}
|
||||
- {id: 12, cat: TV/Anime, desc: "Аниме"}
|
||||
- {id: 16, cat: Books, desc: "Книги"}
|
||||
- {id: 18, cat: Audio/Video, desc: "Клипы / Ролики"}
|
||||
- {id: 22, cat: TV, desc: "ТВ / Передачи"}
|
||||
- {id: 27, cat: PC/Mobile-Other, desc: "Игры - Мобила / КПК"}
|
||||
- {id: 1, cat: PC/ISO, desc: "Образы"}
|
||||
- {id: 4, cat: Other, desc: "Другое"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
@@ -117,24 +115,20 @@ search:
|
||||
args: ["[^a-zA-Z0-9]+", "%"]
|
||||
|
||||
rows:
|
||||
selector: table.table > tbody > tr.torcontduo
|
||||
selector: div.torrent-card
|
||||
|
||||
fields:
|
||||
category:
|
||||
selector: a[href^="browse.php?cat="]
|
||||
attribute: href
|
||||
categorydesc:
|
||||
selector: div.category-badge
|
||||
optional: true
|
||||
default: 4
|
||||
default: Другое
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
- name: replace
|
||||
args: ["---", "Другое"]
|
||||
title:
|
||||
selector: a[href^="details.php?id="]
|
||||
selector: a.torrent-title-link
|
||||
attribute: title
|
||||
filters:
|
||||
- name: regexp
|
||||
args: \'>(.+?)</div
|
||||
- name: htmldecode
|
||||
# normalize to SXXEYY format
|
||||
- name: re_replace
|
||||
args: ["(?i)[CС]езоны?[\\s:]*(\\d+(?:-\\d+)?).+?(?:\\s*(?:[CС]ери[ияй]|Эпизод|Выпуски?))[\\s:]*(\\d+(?:-\\d+)?)\\s*из\\s*(\\w?)", "S$1E$2 of $3"]
|
||||
@@ -193,16 +187,19 @@ search:
|
||||
- name: append
|
||||
args: "{{ if .Config.addrussiantotitle }} RUS{{ else }}{{ end }}"
|
||||
details:
|
||||
selector: a[href^="details.php?id="]
|
||||
selector: a.torrent-title-link
|
||||
attribute: href
|
||||
# there is either a magnet or a download link
|
||||
magnet:
|
||||
selector: a[href^="magnet:?xt="]
|
||||
attribute: href
|
||||
optional: true
|
||||
download:
|
||||
selector: a[href^="details.php?id="]
|
||||
selector: a[href^="download.php?id="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["details", "download"]
|
||||
optional: true
|
||||
poster:
|
||||
selector: img.s
|
||||
selector: img.torrent-poster
|
||||
attribute: src
|
||||
imdbid:
|
||||
selector: a[href^="browse.php?imdb="]
|
||||
@@ -211,46 +208,26 @@ search:
|
||||
- name: querystring
|
||||
args: imdb
|
||||
size:
|
||||
selector: td:nth-child(4)
|
||||
selector: div.size-section
|
||||
seeders:
|
||||
selector: span[title="Раздают"]
|
||||
selector: span.peers-seeders
|
||||
leechers:
|
||||
selector: span[title="Качают"]
|
||||
date_day:
|
||||
# Сегодня в 18:22
|
||||
# Вчера в 20:52
|
||||
selector: a[href^="browse.php?date="]:contains("Сегодня"), a[href^="browse.php?date="]:contains("Вчера")
|
||||
optional: true
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "((Вчера в|Сегодня в)( \\d{2}:\\d{2}))"
|
||||
- name: replace
|
||||
args: ["Сегодня в", "Today"]
|
||||
- name: replace
|
||||
args: ["Вчера в", "Yesterday"]
|
||||
- name: fuzzytime
|
||||
date_year:
|
||||
# 23:48 24/07
|
||||
selector: a[href^="browse.php?date="]:contains("/")
|
||||
optional: true
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "(\\d{2}:\\d{2} \\d{2}/\\d{2})"
|
||||
- name: append
|
||||
args: " +03:00" # MSK
|
||||
- name: dateparse
|
||||
args: "HH:mm dd/MM zzz"
|
||||
selector: span.peers-leechers
|
||||
grabs:
|
||||
selector: span:has(i.fa-download)
|
||||
date:
|
||||
text: "{{ if or .Result.date_year .Result.date_day }}{{ or .Result.date_year .Result.date_day }}{{ else }}now{{ end }}"
|
||||
selector: div.added-date
|
||||
filters:
|
||||
- name: timeago
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[src="/pic/freedownload.gif"]: 0
|
||||
span.bg-success: 0
|
||||
"*": 1
|
||||
uploadvolumefactor:
|
||||
text: 1
|
||||
minimumratio:
|
||||
text: 1.0
|
||||
description:
|
||||
selector: a[href*="?tag="]
|
||||
selector: a.tag
|
||||
attribute: title
|
||||
# engine n/a
|
||||
|
||||
@@ -35,10 +35,12 @@ caps:
|
||||
- {id: Anime, cat: TV/Anime, desc: Anime}
|
||||
- {id: AnimeAudioLossless, cat: Audio/Lossless, desc: "Anime Audio Lossless"}
|
||||
- {id: "AnimeAudio[Lossless]", cat: Audio/Lossless, desc: "Anime Audio Lossless"}
|
||||
- {id: "AnimeAudio[Lossy]", cat: Audio/MP3, desc: "Anime Audio Lossy"}
|
||||
- {id: AnimeEnglish-translated, cat: TV/Anime, desc: "Anime English-translated"}
|
||||
- {id: "AnimeLiveAction[English-translated]", cat: TV/Anime, desc: "Anime Live Action English-translated"}
|
||||
- {id: "AnimeLiveAction[Non-English]", cat: TV/Anime, desc: "Anime Live Action Non-English"}
|
||||
- {id: "AnimeLiveAction[Raw]", cat: TV/Anime, desc: "Anime Live Action Raw"}
|
||||
- {id: AnimePictures, cat: Other, desc: Anime Pictures}
|
||||
- {id: AnimeRaw, cat: TV/Anime, desc: Anime Raw}
|
||||
- {id: AnimeSubs, cat: TV/Anime, desc: Anime Subs}
|
||||
- {id: Apps, cat: PC, desc: Apps}
|
||||
|
||||
@@ -281,6 +281,7 @@ caps:
|
||||
- {id: 2281, cat: PC/ISO, desc: "Web Security Dojo"}
|
||||
- {id: 2246, cat: PC/ISO, desc: "Whonix"}
|
||||
- {id: 2155, cat: PC/ISO, desc: "Wifislax"}
|
||||
- {id: 2229, cat: PC/ISO, desc: "XiVa Studio"}
|
||||
- {id: 2251, cat: PC/ISO, desc: "XigmaNAS"}
|
||||
- {id: 607, cat: PC/ISO, desc: "Xubuntu"}
|
||||
- {id: 612, cat: PC/ISO, desc: "Zen"}
|
||||
|
||||
@@ -182,4 +182,4 @@ search:
|
||||
description:
|
||||
selector: td.rowfollow:nth-child(2)
|
||||
remove: a, b, font, img, span
|
||||
# NexusPHP v1.9.9 2025-10-02
|
||||
# NexusPHP v1.9.11 2025-11-02
|
||||
|
||||
@@ -32,7 +32,7 @@ search:
|
||||
paths:
|
||||
- path: search
|
||||
inputs:
|
||||
q: "{{ if .Keywords }}{{ .Keywords }}{{ else }}*{{ end }}"
|
||||
query: "{{ if .Keywords }}{{ .Keywords }}{{ else }}*{{ end }}"
|
||||
|
||||
rows:
|
||||
selector: a.list-group-item
|
||||
|
||||
@@ -31,7 +31,7 @@ caps:
|
||||
- {id: 62, cat: Movies/UHD, desc: "Film/HU/UHD"}
|
||||
- {id: 4, cat: PC/Games, desc: "Játékok/ISO"}
|
||||
- {id: 39, cat: PC/Games, desc: "Játékok/Rip/Dox"}
|
||||
# - {id: 47, cat: Other, desc: "Klippek"}
|
||||
- {id: 47, cat: Other, desc: "Klippek"}
|
||||
- {id: 30, cat: Audio/Lossless, desc: "Lossless/EN"}
|
||||
# - {id: 29, cat: Audio/Lossless, desc: "Lossless/HU"}
|
||||
- {id: 25, cat: Audio/MP3, desc: "MP3/EN"}
|
||||
@@ -141,7 +141,7 @@ search:
|
||||
img[src="/pic/categories/uhd_hun.png"]: 62
|
||||
img[src="/pic/categories/021.png"]: 4
|
||||
img[src="/pic/categories/cat_games.gif"]: 39
|
||||
# img[]: 47
|
||||
img[src="/pic/categories/026.png"]: 47
|
||||
img[src="/pic/categories/024.png"]: 30
|
||||
# img[]: 29
|
||||
img[src="/pic/categories/023.png"]: 25
|
||||
|
||||
@@ -23,19 +23,11 @@ caps:
|
||||
search: [q]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
- name: cookie
|
||||
type: text
|
||||
label: Username
|
||||
- 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 NicePT Web Site. Otherwise just leave it empty."
|
||||
label: Cookie
|
||||
- name: info_cookie
|
||||
type: info_cookie
|
||||
- name: freeleech
|
||||
type: checkbox
|
||||
label: Search freeleech only
|
||||
@@ -66,20 +58,10 @@ settings:
|
||||
default: "Account retention rules:<ol><li>Veteran Users users and above will be retained forever</li><li>Elite Users and above will not have their account deleted after parking (in the Control Panel)</li><li>Users with a parked account will be deleted if they do not log in for 400 consecutive days</li><li>Users with a non-parked account will be banned if they do not log in for 40 consecutive days</li><li>Users with no traffic (ie, upload/download data are both 0) will be banned if they do not log in for 7 consecutive days.</li></ol>"
|
||||
|
||||
login:
|
||||
path: login.php
|
||||
method: form
|
||||
form: form[action="takelogin.php"]
|
||||
captcha:
|
||||
type: image
|
||||
selector: img[alt="CAPTCHA"]
|
||||
input: imagestring
|
||||
# using cookie method because site does a JS call to API/Challenge via JQuery to load response parm required for takelogin.php
|
||||
method: cookie
|
||||
inputs:
|
||||
secret: ""
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
two_step_code: "{{ .Config.2facode }}"
|
||||
error:
|
||||
- selector: td.embedded:has(h2:contains("失败"))
|
||||
cookie: "{{ .Config.cookie }}"
|
||||
test:
|
||||
path: index.php
|
||||
selector: a[href="logout.php"]
|
||||
@@ -187,4 +169,4 @@ search:
|
||||
filters:
|
||||
- name: prepend
|
||||
args: "{{ .Result.description_verified }} "
|
||||
# NexusPHP v1.9.4 2025-06-01
|
||||
# NexusPHP v1.9.11 2025-11-02
|
||||
|
||||
@@ -10,8 +10,7 @@ type: public
|
||||
encoding: UTF-8
|
||||
# to fetch current domain use https://www.rantop.org/
|
||||
links:
|
||||
- https://www.nortorrent.town/
|
||||
- https://nortorrent-proxy.site/
|
||||
- https://ww1-nortorrent.me/
|
||||
legacylinks:
|
||||
# latest domains list
|
||||
- https://www.rantop.org/
|
||||
@@ -27,8 +26,6 @@ legacylinks:
|
||||
- https://www.site-torrent.com/
|
||||
- https://www.rantop.my/
|
||||
# actual legacylinks
|
||||
- https://www.torrent9.run/
|
||||
- https://www.torrent9.cv/
|
||||
- https://www.torrent9.ke/
|
||||
- https://www.torrent9.gdn/
|
||||
- https://www.torrent9.men/
|
||||
@@ -42,6 +39,8 @@ legacylinks:
|
||||
- https://www.nortorrent5.com/
|
||||
- https://www.nortorrent6.com/
|
||||
- https://www.nortorrent.net/
|
||||
- https://www.nortorrent.town/
|
||||
- https://nortorrent-proxy.site/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
||||
@@ -183,4 +183,4 @@ search:
|
||||
description:
|
||||
selector: td:nth-child(2)
|
||||
remove: a, b, font, img, span
|
||||
# NexusPHP v1.9.6 2025-06-25
|
||||
# NexusPHP v1.9.11 2025-11-02
|
||||
|
||||
@@ -194,4 +194,4 @@ search:
|
||||
description:
|
||||
selector: td.rowfollow:nth-child(2)
|
||||
remove: a, b, font, img, span
|
||||
# NexusPHP v1.9.6 2025-06-25
|
||||
# NexusPHP v1.9.11 2025-11-02
|
||||
|
||||
@@ -227,4 +227,4 @@ search:
|
||||
filters:
|
||||
- name: prepend
|
||||
args: "{{ .Result.description_verified }} "
|
||||
# NexusPHP v1.9.3 2025-05-24
|
||||
# NexusPHP v1.9.11 2025-11-02
|
||||
|
||||
@@ -9,38 +9,38 @@ type: semi-private
|
||||
encoding: windows-1251
|
||||
followredirect: true
|
||||
links:
|
||||
- https://nov14.rudub.pics/
|
||||
- https://nov27.rudub.world/
|
||||
legacylinks:
|
||||
- https://oct30.rudub.homes/
|
||||
- http://oct31.rudub.homes/
|
||||
- https://oct31.rudub.homes/
|
||||
- http://nov01.rudub.homes/
|
||||
- https://nov01.rudub.homes/
|
||||
- http://nov02.rudub.homes/
|
||||
- https://nov02.rudub.homes/
|
||||
- http://nov03.rudub.homes/
|
||||
- https://nov03.rudub.homes/
|
||||
- http://nov04.rudub.homes/
|
||||
- https://nov04.rudub.homes/
|
||||
- http://nov05.rudub.homes/
|
||||
- https://nov05.rudub.homes/
|
||||
- http://nov06.rudub.homes/
|
||||
- https://nov06.rudub.homes/
|
||||
- http://nov07.rudub.pics/
|
||||
- https://nov07.rudub.pics/
|
||||
- http://nov08.rudub.pics/
|
||||
- https://nov08.rudub.pics/
|
||||
- http://nov09.rudub.pics/
|
||||
- https://nov09.rudub.pics/
|
||||
- http://nov10.rudub.pics/
|
||||
- https://nov10.rudub.pics/
|
||||
- http://nov11.rudub.pics/
|
||||
- https://nov11.rudub.pics/
|
||||
- http://nov12.rudub.pics/
|
||||
- https://nov12.rudub.pics/
|
||||
- http://nov13.rudub.pics/
|
||||
- https://nov13.rudub.pics/
|
||||
- http://nov14.rudub.pics/
|
||||
- https://nov14.rudub.pics/
|
||||
- http://nov15.rudub.pics/
|
||||
- https://nov15.rudub.pics/
|
||||
- http://nov16.rudub.pics/
|
||||
- https://nov16.rudub.pics/
|
||||
- http://nov17.rudub.pics/
|
||||
- https://nov17.rudub.pics/
|
||||
- http://nov18.rudub.pics/
|
||||
- https://nov18.rudub.pics/
|
||||
- http://nov19.rudub.pics/
|
||||
- https://nov19.rudub.pics/
|
||||
- http://nov20.rudub.pics/
|
||||
- https://nov20.rudub.pics/
|
||||
- http://nov21.rudub.pics/
|
||||
- https://nov21.rudub.pics/
|
||||
- http://nov22.rudub.pics/
|
||||
- https://nov22.rudub.pics/
|
||||
- http://nov23.rudub.pics/
|
||||
- https://nov23.rudub.pics/
|
||||
- http://nov24.rudub.pics/
|
||||
- https://nov24.rudub.pics/
|
||||
- http://nov25.rudub.world/
|
||||
- https://nov25.rudub.world/
|
||||
- http://nov26.rudub.world/
|
||||
- https://nov26.rudub.world/
|
||||
- http://nov27.rudub.world/
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 1, cat: TV, desc: "TV"}
|
||||
|
||||
@@ -6,10 +6,11 @@ language: ru-RU
|
||||
type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://sosulki.com/
|
||||
- https://sosulki.hlom.ru/
|
||||
legacylinks:
|
||||
- http://sosulki.net/
|
||||
- http://sosulki.com/ # site forces https
|
||||
- https://sosulki.com/
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 12, cat: XXX, desc: "Порно категории"}
|
||||
@@ -46,7 +47,7 @@ search:
|
||||
- path: "{{ if .Keywords }}?do=search&subaction=search&search_start=0&full_search=1&result_from=1&story={{ .Keywords }}&titleonly=3&catlist[]=0&searchdate=0&resorder=desc{{ else }}lastnews/{{ end }}"
|
||||
|
||||
rows:
|
||||
selector: tr.fr_viewn_in:has(td.frs:contains("B"))
|
||||
selector: tr.fr_viewn_in
|
||||
|
||||
fields:
|
||||
category:
|
||||
|
||||
@@ -40,6 +40,10 @@ settings:
|
||||
type: checkbox
|
||||
label: Search freeleech only
|
||||
default: false
|
||||
- name: tmdbidonly
|
||||
type: checkbox
|
||||
label: Disable IMDB and TVDB ID search (only support TMDB ID) to potentially improve Sonarr and Radarr results
|
||||
default: false
|
||||
- name: multilang
|
||||
type: checkbox
|
||||
label: Replace MULTi by another language in release name
|
||||
@@ -110,9 +114,9 @@ search:
|
||||
name: "{{ .Keywords }}"
|
||||
seasonNumber: "{{ .Query.Season }}"
|
||||
episodeNumber: "{{ .Query.Ep }}"
|
||||
imdbId: "{{ .Query.IMDBIDShort }}"
|
||||
imdbId: "{{ if .Config.tmdbidonly }}{{ else }}{{ .Query.IMDBIDShort }}{{ end }}"
|
||||
tmdbId: "{{ .Query.TMDBID }}"
|
||||
tvdbId: "{{ .Query.TVDBID }}"
|
||||
tvdbId: "{{ if .Config.tmdbidonly }}{{ else }}{{ .Query.TVDBID }}{{ end }}"
|
||||
"free[]": "{{ if .Config.freeleech }}100{{ else }}{{ end }}"
|
||||
sortField: "{{ .Config.sort }}"
|
||||
sortDirection: "{{ .Config.type }}"
|
||||
|
||||
@@ -36,7 +36,7 @@ settings:
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: "search/{{ if .Keywords }}{{ .Keywords }}{{ else }}{{ .Today.Year }}{{ end }}"
|
||||
- path: "search/{{ if .Keywords }}{{ .Keywords }}{{ else }}{{ .Today.Year }}{{ end }}/"
|
||||
|
||||
rows:
|
||||
selector: table#archiveResult tbody tr:has(a[href^="magnet:?xt="])
|
||||
|
||||
@@ -7,12 +7,11 @@ type: public
|
||||
encoding: UTF-8
|
||||
followredirect: true
|
||||
links:
|
||||
- https://torrentqq394.com/
|
||||
- https://torrentqq395.com/
|
||||
- https://torrentegg83.com/
|
||||
legacylinks:
|
||||
- https://torrentegg68.com/
|
||||
- https://torrentegg69.com/
|
||||
- https://torrentqq379.com/
|
||||
- https://torrentegg70.com/
|
||||
- https://torrentqq380.com/
|
||||
- https://torrentegg71.com/
|
||||
@@ -40,6 +39,7 @@ legacylinks:
|
||||
- https://torrentqq392.com/
|
||||
- https://torrentegg82.com/
|
||||
- https://torrentqq393.com/
|
||||
- https://torrentqq394.com/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
||||
@@ -9,10 +9,8 @@ followredirect: true
|
||||
requestDelay: 2
|
||||
# to fetch current domain use https://tzip.top/
|
||||
links:
|
||||
- https://torrentsome221.com/
|
||||
- https://torrentsome223.com/
|
||||
legacylinks:
|
||||
- https://torrentsome206.com/
|
||||
- https://torrentsome207.com/
|
||||
- https://torrentsome208.com/
|
||||
- https://torrentsome209.com/
|
||||
- https://torrentsome210.com/
|
||||
@@ -26,6 +24,8 @@ legacylinks:
|
||||
- https://torrentsome218.com/
|
||||
- https://torrentsome219.com/
|
||||
- https://torrentsome220.com/
|
||||
- https://torrentsome221.com/
|
||||
- https://torrentsome222.com/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
||||
@@ -9,10 +9,8 @@ followredirect: true
|
||||
requestDelay: 2
|
||||
# to fetch current domain use https://tzip.top/
|
||||
links:
|
||||
- https://torrenttip202.top/
|
||||
- https://torrenttip204.top/
|
||||
legacylinks:
|
||||
- https://torrenttip187.top/
|
||||
- https://torrenttip188.top/
|
||||
- https://torrenttip189.top/
|
||||
- https://torrenttip190.top/
|
||||
- https://torrenttip191.top/
|
||||
@@ -26,6 +24,8 @@ legacylinks:
|
||||
- https://torrenttip199.top/
|
||||
- https://torrenttip200.top/
|
||||
- https://torrenttip201.top/
|
||||
- https://torrenttip202.top/
|
||||
- https://torrenttip203.top/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
||||
@@ -171,4 +171,4 @@ search:
|
||||
description:
|
||||
selector: td:nth-child(2)
|
||||
remove: a, b, font, img, span
|
||||
# NexusPHP v1.9.10 2025-10-30
|
||||
# NexusPHP v1.9.11 2025-11-02
|
||||
|
||||
@@ -31,10 +31,8 @@ caps:
|
||||
- {id: 190, cat: Movies, desc: " |- Фильмы в 4K и 3D"}
|
||||
- {id: 34, cat: Movies, desc: " |- Перевод на узбекский"}
|
||||
- {id: 25, cat: Movies, desc: "Узбекские кинофильмы"}
|
||||
- {id: 32, cat: Movies, desc: " |- Новинки"}
|
||||
- {id: 30, cat: Movies, desc: " |- Фильмы 2011-2024 годов"}
|
||||
- {id: 29, cat: Movies, desc: " |- Фильмы 2000-2010 годов"}
|
||||
- {id: 26, cat: Movies, desc: " |- Фильмы до 2000 года"}
|
||||
- {id: 32, cat: Movies, desc: " |- Пр-во Узбекфильм (на русском)"}
|
||||
- {id: 30, cat: Movies, desc: " |- Пр-во Узбекфильм (на узбекском языке)"}
|
||||
# Сериалы, Видео и ТВ # Series, Videos and TV
|
||||
- {id: 97, cat: TV, desc: "Сериалы"}
|
||||
- {id: 333, cat: TV, desc: " |- Игра престолов / Game of Thrones"}
|
||||
@@ -85,6 +83,7 @@ caps:
|
||||
- {id: 132, cat: Audio, desc: " |- Классическая музыа"}
|
||||
- {id: 125, cat: Audio, desc: " |- New Age, Relax, Meditative & Flamenco"}
|
||||
- {id: 124, cat: Audio, desc: " |- Фольклор, Народная и Этническая музыка"}
|
||||
- {id: 338, cat: Audio, desc: " |- Country"}
|
||||
- {id: 231, cat: Audio, desc: " |- Сборники и альбомы выходившие неофициальными изданиями."}
|
||||
- {id: 144, cat: Audio, desc: "♫ ROCK & METAL ♫"}
|
||||
- {id: 201, cat: Audio, desc: " |- Русский Rock, Metal (mp3)"}
|
||||
@@ -170,6 +169,7 @@ caps:
|
||||
- {id: 289, cat: PC/Games, desc: " |- Horror"}
|
||||
- {id: 307, cat: PC/Games, desc: " |- Logic"}
|
||||
- {id: 304, cat: PC/Games, desc: " |- Lifestyle"}
|
||||
- {id: 336, cat: PC/Games, desc: " |- Sports"}
|
||||
- {id: 306, cat: PC/Games, desc: " |- Exploration"}
|
||||
- {id: 305, cat: PC/Games, desc: " |- Management"}
|
||||
- {id: 115, cat: PC/Games, desc: " |- Аркады"}
|
||||
|
||||
@@ -8,8 +8,8 @@ encoding: UTF-8
|
||||
requestDelay: 2.5 # 2.5 requests per second (2 causes problems)
|
||||
links:
|
||||
# if the primary domain changes then don't forget to update the details, download and poster replace args
|
||||
- https://yts.mx/
|
||||
# official domain list are at https://yifystatus.com/ and official proxies list are at https://ytsproxies.com/
|
||||
- https://yts.lt/
|
||||
# official domain list are at https://yifystatus.com/ and official proxies list are at https://ytsproxies.com/
|
||||
- https://yts.unblockninja.com/
|
||||
- https://yts.ninjaproxy1.com/
|
||||
- https://yts.proxyninja.org/
|
||||
@@ -17,9 +17,9 @@ links:
|
||||
- https://yts.torrentbay.st/
|
||||
- https://yts.torrentsbay.org/
|
||||
legacylinks:
|
||||
- https://yts.lt/
|
||||
- https://yts.am/
|
||||
- https://yts.ag/
|
||||
- https://yts.mx/
|
||||
- https://yts.mrunblock.bond/
|
||||
- https://yts.nocensor.cloud/
|
||||
- https://yts.unblockit.download/
|
||||
@@ -104,19 +104,19 @@ search:
|
||||
selector: ..url
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["^https?:\\/\\/yts\\.mx\\/", "{{ .Config.sitelink }}"] # fix for 12494
|
||||
args: ["^https?:\\/\\/yts\\.(mx|lt)\\/", "{{ .Config.sitelink }}"] # fix for 12494
|
||||
download:
|
||||
selector: url
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["^https?:\\/\\/yts\\.mx\\/", "{{ .Config.sitelink }}"] # fix for 12494
|
||||
args: ["^https?:\\/\\/yts\\.(mx|lt)\\/", "{{ .Config.sitelink }}"] # fix for 12494
|
||||
infohash:
|
||||
selector: hash
|
||||
poster:
|
||||
selector: ..large_cover_image
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["^https?:\\/\\/yts\\.mx\\/", "{{ .Config.sitelink }}"] # fix for 12494
|
||||
args: ["^https?:\\/\\/yts\\.(mx|lt)\\/", "{{ .Config.sitelink }}"] # fix for 12494
|
||||
imdbid:
|
||||
selector: ..imdb_code
|
||||
date:
|
||||
|
||||
@@ -9,8 +9,7 @@ type: public
|
||||
encoding: UTF-8
|
||||
# to fetch current domain use https://www.rantop.org/
|
||||
links:
|
||||
- https://www.zktorrent.town/
|
||||
- https://zktorrent-proxy.site/
|
||||
- https://ww1-zktorrent.me/
|
||||
legacylinks:
|
||||
# latest domains list
|
||||
- https://www.rantop.org/
|
||||
@@ -26,8 +25,6 @@ legacylinks:
|
||||
- https://www.site-torrent.com/
|
||||
- https://www.rantop.my/
|
||||
# actual legacylinks
|
||||
- https://www.gktorrent.si/
|
||||
- https://www.gktorrent.my/
|
||||
- https://www.gktorrent.cz/
|
||||
- https://www.gktorrent.ke/
|
||||
- https://www.gktorrent.cv/
|
||||
@@ -41,6 +38,8 @@ legacylinks:
|
||||
- https://www.zktorrent5.com/
|
||||
- https://www.zktorrent.net/
|
||||
- https://www.zktorrent6.com/
|
||||
- https://www.zktorrent.town/
|
||||
- https://zktorrent-proxy.site/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
||||
34
src/Jackett.Common/Extensions/HashingExtensions.cs
Normal file
34
src/Jackett.Common/Extensions/HashingExtensions.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Jackett.Common.Extensions
|
||||
{
|
||||
public static class HashingExtensions
|
||||
{
|
||||
public static string SHA1Hash(this string input)
|
||||
{
|
||||
using var hash = SHA1.Create();
|
||||
|
||||
return GetHash(hash.ComputeHash(Encoding.UTF8.GetBytes(input)));
|
||||
}
|
||||
|
||||
public static string SHA256Hash(this string input)
|
||||
{
|
||||
using var hash = SHA256.Create();
|
||||
|
||||
return GetHash(hash.ComputeHash(Encoding.UTF8.GetBytes(input)));
|
||||
}
|
||||
|
||||
private static string GetHash(byte[] bytes)
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
|
||||
foreach (var b in bytes)
|
||||
{
|
||||
stringBuilder.Append(b.ToString("x2"));
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,8 @@ using System.Xml.Linq;
|
||||
using Jackett.Common.Extensions;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig.Bespoke;
|
||||
using Jackett.Common.Serializer;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Jackett.Common.Utils.Clients;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
@@ -204,44 +202,36 @@ namespace Jackett.Common.Indexers.Definitions
|
||||
seasonPart = Regex.Replace(seasonPart, @"\b\d{4}\b$", "");
|
||||
var hasPartNumber = Regex.IsMatch(seasonPart, @"\bPart\s+\d+\b", RegexOptions.IgnoreCase);
|
||||
var seasonMatch = Regex.Match(seasonPart,
|
||||
@"\b(?:Season|S|Series)\s*(\d+)|\b(\d+)(?:st|nd|rd|th)?\s*Season\b|\b([IVXLCDM]+)\b|\b(\d+)\b",
|
||||
@"\b(?:Season|S|Series)\s*(?<season_number>\d+)|\b(?<season_number>\d+)(?:st|nd|rd|th)?\s*Season\b|\b(?<roman_number>M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3}))\b|\b(?<season_number>\d+)\b",
|
||||
RegexOptions.IgnoreCase);
|
||||
var season = "S01";
|
||||
|
||||
if (seasonMatch.Success && !hasPartNumber)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(seasonMatch.Groups[1].Value))
|
||||
if (seasonMatch.Groups["season_number"].Success
|
||||
&& !string.IsNullOrWhiteSpace(seasonMatch.Groups["season_number"].Value)
|
||||
&& int.TryParse(seasonMatch.Groups["season_number"].Value, out var seasonNumber))
|
||||
{
|
||||
season = $"S{int.Parse(seasonMatch.Groups[1].Value):D2}";
|
||||
season = $"S{seasonNumber:D2}";
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(seasonMatch.Groups[2].Value))
|
||||
{
|
||||
season = $"S{int.Parse(seasonMatch.Groups[2].Value):D2}";
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(seasonMatch.Groups[3].Value))
|
||||
else if (seasonMatch.Groups["roman_number"].Success && !string.IsNullOrWhiteSpace(seasonMatch.Groups["roman_number"].Value))
|
||||
{
|
||||
season = $"S{RomanToArabic(seasonMatch.Groups[3].Value):D2}";
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(seasonMatch.Groups[4].Value))
|
||||
{
|
||||
season = $"S{int.Parse(seasonMatch.Groups[4].Value):D2}";
|
||||
}
|
||||
}
|
||||
|
||||
var episodes = string.Empty;
|
||||
var epMatch = Regex.Match(episodesPart, @"(\d+)(?:[-–—](\d+))?");
|
||||
|
||||
if (epMatch.Success)
|
||||
if (epMatch.Success && int.TryParse(epMatch.Groups[1].Value, out var episodeStartNumber))
|
||||
{
|
||||
var start = int.Parse(epMatch.Groups[1].Value);
|
||||
if (epMatch.Groups[2].Success)
|
||||
{
|
||||
var end = int.Parse(epMatch.Groups[2].Value);
|
||||
episodes = $"E{start:D2}-E{end:D2}";
|
||||
episodes = $"E{episodeStartNumber:D2}-E{int.Parse(epMatch.Groups[2].Value):D2}";
|
||||
}
|
||||
else
|
||||
{
|
||||
episodes = $"E{start:D2}";
|
||||
episodes = $"E{episodeStartNumber:D2}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,11 +240,12 @@ namespace Jackett.Common.Indexers.Definitions
|
||||
|
||||
private static int RomanToArabic(string roman)
|
||||
{
|
||||
int[] values = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
|
||||
string[] numerals = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
|
||||
roman = roman.ToUpperInvariant();
|
||||
|
||||
var values = new[] { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
|
||||
var numerals = new[] { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
|
||||
var result = 0;
|
||||
var i = 0;
|
||||
roman = roman.ToUpper();
|
||||
while (roman.Length > 0)
|
||||
{
|
||||
if (roman.StartsWith(numerals[i]))
|
||||
|
||||
@@ -249,13 +249,14 @@ namespace Jackett.Common.Indexers.Definitions
|
||||
var details = new Uri(qDetailsLink.GetAttribute("href"));
|
||||
|
||||
var qPosterLink = row.QuerySelector("img[src*=\"/torrents/images/\"]");
|
||||
var qMagnetLink = row.QuerySelector("a[href^=\"magnet:?xt=\"]");
|
||||
var size = ParseUtil.GetBytes(row.QuerySelector("td:nth-last-child(5)").TextContent);
|
||||
var matchDateAdded = Regex.Match(row.QuerySelector(" td:nth-child(2)").TextContent, @"(\d{2}-\d{2}-\d{4} \d{2}:\d{2})", RegexOptions.IgnoreCase);
|
||||
var publishDate = matchDateAdded.Groups[1].Success && DateTime.TryParseExact(matchDateAdded.Groups[1].Value, "dd-MM-yyyy HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var parsedDate) ? parsedDate : DateTime.Now;
|
||||
|
||||
var grabs = ParseUtil.CoerceInt(row.QuerySelector("td:nth-last-child(4)").TextContent);
|
||||
var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-last-child(3)").TextContent);
|
||||
var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-last-child(2)").TextContent) + seeders;
|
||||
var grabs = ParseUtil.CoerceLong(row.QuerySelector("td:nth-last-child(4)").TextContent);
|
||||
var seeders = ParseUtil.CoerceLong(row.QuerySelector("td:nth-last-child(3)").TextContent);
|
||||
var leechers = ParseUtil.CoerceLong(row.QuerySelector("td:nth-last-child(2)").TextContent) + seeders;
|
||||
|
||||
var dlVolumeFactor = 1.0;
|
||||
var upVolumeFactor = 1.0;
|
||||
@@ -287,6 +288,10 @@ namespace Jackett.Common.Indexers.Definitions
|
||||
{
|
||||
release.Poster = new Uri(qPosterLink.GetAttribute("src"));
|
||||
}
|
||||
if (qMagnetLink != null)
|
||||
{
|
||||
release.MagnetUri = new Uri(qMagnetLink.GetAttribute("href"));
|
||||
}
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace Jackett.Common.Indexers.Definitions
|
||||
var maxPages = 2; // we scrape only 2 pages for recent torrents
|
||||
if (!string.IsNullOrWhiteSpace(query.GetQueryString()))
|
||||
{
|
||||
searchString = Uri.EscapeUriString(query.GetQueryString());
|
||||
searchString = Uri.EscapeDataString(query.GetQueryString());
|
||||
maxPages = MaxSearchPageLimit;
|
||||
}
|
||||
|
||||
|
||||
@@ -163,15 +163,18 @@ namespace Jackett.Common.Indexers.Definitions
|
||||
{
|
||||
throw new Exception("Error, the Download link at the requested path does not exist.");
|
||||
}
|
||||
var wmDoc = new HtmlParser().ParseDocument(wmPage.ContentString);
|
||||
|
||||
var wmHtmlParser = new HtmlParser();
|
||||
var wmDoc = await wmHtmlParser.ParseDocumentAsync(wmPage.ContentString);
|
||||
var enlacitoUrl = wmDoc.QuerySelector(".app-message a:not(.buttonPassword)")?.GetAttribute("href");
|
||||
|
||||
var enlacitoPage = await RequestWithCookiesAndRetryAsync(enlacitoUrl, referer: SiteLink);
|
||||
var enlacitoDoc = new HtmlParser().ParseDocument(enlacitoPage.ContentString);
|
||||
|
||||
var enlacitoHtmlParser = new HtmlParser();
|
||||
var enlacitoDoc = await enlacitoHtmlParser.ParseDocumentAsync(enlacitoPage.ContentString);
|
||||
var enlacitoFormUrl = enlacitoDoc.QuerySelector("form").GetAttribute("action");
|
||||
var enlacitoFormLinkser = enlacitoDoc.QuerySelector("input[name=\"linkser\"]").GetAttribute("value");
|
||||
|
||||
|
||||
var body = new Dictionary<string, string>
|
||||
{
|
||||
{ "linkser", enlacitoFormLinkser }
|
||||
@@ -182,7 +185,7 @@ namespace Jackett.Common.Indexers.Definitions
|
||||
|
||||
var linkOut = v.Groups[1].ToString();
|
||||
var slink = Encoding.UTF8.GetString(Convert.FromBase64String(linkOut));
|
||||
var ulink = OpenSSLDecrypt(slink, TorrentLinkEncryptionKey);
|
||||
var ulink = await OpenSSLDecryptAsync(slink, TorrentLinkEncryptionKey);
|
||||
|
||||
var result = await RequestWithCookiesAndRetryAsync(ulink);
|
||||
return result.ContentBytes;
|
||||
@@ -463,7 +466,7 @@ namespace Jackett.Common.Indexers.Definitions
|
||||
}
|
||||
|
||||
// Thanks to https://stackoverflow.com/a/5454692/2078070 !!!
|
||||
private string OpenSSLDecrypt(string encrypted, string passphrase)
|
||||
private async Task<string> OpenSSLDecryptAsync(string encrypted, string passphrase)
|
||||
{
|
||||
// base 64 decode
|
||||
var encryptedBytesWithSalt = Convert.FromBase64String(encrypted);
|
||||
@@ -477,7 +480,7 @@ namespace Jackett.Common.Indexers.Definitions
|
||||
// get key and iv
|
||||
DeriveKeyAndIV(passphrase, salt, out var key, out var iv);
|
||||
|
||||
return DecryptStringFromBytesAes(encryptedBytes, key, iv);
|
||||
return await DecryptStringFromBytesAesAsync(encryptedBytes, key, iv);
|
||||
}
|
||||
|
||||
private void DeriveKeyAndIV(string passphrase, byte[] salt, out byte[] key, out byte[] iv)
|
||||
@@ -518,7 +521,7 @@ namespace Jackett.Common.Indexers.Definitions
|
||||
md5 = null;
|
||||
}
|
||||
|
||||
private string DecryptStringFromBytesAes(byte[] cipherText, byte[] key, byte[] iv)
|
||||
private static async Task<string> DecryptStringFromBytesAesAsync(byte[] cipherText, byte[] key, byte[] iv)
|
||||
{
|
||||
if (cipherText == null || cipherText.Length <= 0)
|
||||
{
|
||||
@@ -535,44 +538,17 @@ namespace Jackett.Common.Indexers.Definitions
|
||||
throw new ArgumentNullException(nameof(iv));
|
||||
}
|
||||
|
||||
// Declare the RijndaelManaged object
|
||||
// used to decrypt the data.
|
||||
RijndaelManaged aesAlg = null;
|
||||
using var aesAlg = Aes.Create();
|
||||
aesAlg.Key = key;
|
||||
aesAlg.IV = iv;
|
||||
|
||||
// Declare the string used to hold
|
||||
// the decrypted text.
|
||||
string plaintext;
|
||||
var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
|
||||
|
||||
try
|
||||
{
|
||||
// Create a RijndaelManaged object
|
||||
// with the specified key and IV.
|
||||
aesAlg = new RijndaelManaged { Mode = CipherMode.CBC, KeySize = 256, BlockSize = 128, Key = key, IV = iv };
|
||||
using var msDecrypt = new MemoryStream(cipherText);
|
||||
using var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
|
||||
using var srDecrypt = new StreamReader(csDecrypt);
|
||||
|
||||
// Create a decrytor to perform the stream transform.
|
||||
var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
|
||||
// Create the streams used for decryption.
|
||||
using (var msDecrypt = new MemoryStream(cipherText))
|
||||
{
|
||||
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
|
||||
{
|
||||
using (var srDecrypt = new StreamReader(csDecrypt))
|
||||
{
|
||||
// Read the decrypted bytes from the decrypting stream
|
||||
// and place them in a string.
|
||||
plaintext = srDecrypt.ReadToEnd();
|
||||
srDecrypt.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Clear the RijndaelManaged object.
|
||||
aesAlg?.Clear();
|
||||
}
|
||||
|
||||
return plaintext;
|
||||
return await srDecrypt.ReadToEndAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,11 @@ using System.Collections.Specialized;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Html.Parser;
|
||||
using Jackett.Common.Extensions;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
@@ -253,11 +252,13 @@ namespace Jackett.Common.Indexers.Definitions
|
||||
if (string.IsNullOrWhiteSpace(sessionId))
|
||||
throw new ExceptionWithConfigData("Error getting the Session ID", configData);
|
||||
|
||||
await Task.Delay(3000);
|
||||
|
||||
// The second page send the login with the hash
|
||||
// The hash is reverse engineering from https://www.zonaq.pw/retorno/2/smf/Themes/smf_ZQ/scripts/script.js
|
||||
// doForm.hash_passwrd.value = hex_sha1(hex_sha1(doForm.user.value.php_to8bit().php_strtolower() + doForm.passwrd.value.php_to8bit()) + cur_session_id);
|
||||
Thread.Sleep(3000);
|
||||
var hashPassword = Sha1Hash(Sha1Hash(configData.Username.Value.ToLower() + configData.Password.Value) + sessionId);
|
||||
var hashPassword = $"{(configData.Username.Value.ToLowerInvariant() + configData.Password.Value).SHA1Hash()}{sessionId}".SHA1Hash();
|
||||
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "user", configData.Username.Value },
|
||||
{ "passwrd", configData.Password.Value },
|
||||
@@ -288,11 +289,5 @@ namespace Jackett.Common.Indexers.Definitions
|
||||
Thread.Sleep(3000);
|
||||
await RequestWithCookiesAsync(Login4Url);
|
||||
}
|
||||
|
||||
private static string Sha1Hash(string input)
|
||||
{
|
||||
var hash = new SHA1Managed().ComputeHash(Encoding.UTF8.GetBytes(input));
|
||||
return string.Concat(hash.Select(b => b.ToString("x2")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Jackett.Common.Extensions;
|
||||
using Jackett.Common.Indexers;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.Config;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Jackett.Common.Utils;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
|
||||
@@ -36,8 +34,7 @@ namespace Jackett.Common.Services
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
private readonly ServerConfig _serverConfig;
|
||||
private readonly SHA256Managed _sha256 = new SHA256Managed();
|
||||
private readonly Dictionary<string, TrackerCache> _cache = new Dictionary<string, TrackerCache>();
|
||||
private readonly Dictionary<string, TrackerCache> _cache = new();
|
||||
|
||||
public CacheService(Logger logger, ServerConfig serverConfig)
|
||||
{
|
||||
@@ -236,11 +233,11 @@ namespace Jackett.Common.Services
|
||||
}
|
||||
}
|
||||
|
||||
private string GetQueryHash(TorznabQuery query)
|
||||
private static string GetQueryHash(TorznabQuery query)
|
||||
{
|
||||
var json = GetSerializedQuery(query);
|
||||
// Compute the hash
|
||||
return BitConverter.ToString(_sha256.ComputeHash(Encoding.UTF8.GetBytes(json)));
|
||||
|
||||
return json.SHA256Hash();
|
||||
}
|
||||
|
||||
private static string GetSerializedQuery(TorznabQuery query)
|
||||
|
||||
@@ -51,7 +51,8 @@ namespace Jackett.Common.Services
|
||||
if (!Directory.Exists(GetAppDataFolder()))
|
||||
{
|
||||
var dir = Directory.CreateDirectory(GetAppDataFolder());
|
||||
if (Environment.OSVersion.Platform != PlatformID.Unix)
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
var access = dir.GetAccessControl();
|
||||
var directorySecurity = new DirectorySecurity(GetAppDataFolder(), AccessControlSections.All);
|
||||
@@ -126,7 +127,7 @@ namespace Jackett.Common.Services
|
||||
if (!Directory.Exists(destFolder))
|
||||
{
|
||||
var dir = Directory.CreateDirectory(destFolder);
|
||||
if (Environment.OSVersion.Platform != PlatformID.Unix)
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
var directorySecurity = new DirectorySecurity(destFolder, AccessControlSections.All);
|
||||
directorySecurity.AddAccessRule(new FileSystemAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), FileSystemRights.FullControl, InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit, PropagationFlags.None, AccessControlType.Allow));
|
||||
@@ -137,7 +138,7 @@ namespace Jackett.Common.Services
|
||||
{
|
||||
File.Copy(file, destPath);
|
||||
// The old files were created when running as admin so make sure they are editable by normal users / services.
|
||||
if (Environment.OSVersion.Platform != PlatformID.Unix)
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
var fileInfo = new FileInfo(destFolder);
|
||||
var fileSecurity = new FileSecurity(destPath, AccessControlSections.All);
|
||||
|
||||
@@ -74,8 +74,6 @@ namespace Jackett.Common.Services
|
||||
}
|
||||
}
|
||||
|
||||
private bool AcceptCert(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) => true;
|
||||
|
||||
private async Task CheckForUpdates()
|
||||
{
|
||||
logger.Info($"Checking for updates... Jackett variant: {variant}");
|
||||
@@ -180,14 +178,7 @@ namespace Jackett.Common.Services
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Error checking for updates.\n{e}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!isWindows)
|
||||
{
|
||||
System.Net.ServicePointManager.ServerCertificateValidationCallback -= AcceptCert;
|
||||
}
|
||||
logger.Error(e, $"Error checking for updates.\n{e}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Versioning;
|
||||
using System.ServiceProcess;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Jackett.Common.Utils;
|
||||
@@ -9,6 +10,9 @@ using NLog;
|
||||
|
||||
namespace Jackett.Common.Services
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
public class WindowsServiceConfigService : IServiceConfigService
|
||||
{
|
||||
private const string NAME = "Jackett";
|
||||
|
||||
@@ -29,8 +29,13 @@ namespace Jackett.Common.Utils.Clients
|
||||
}
|
||||
|
||||
[DebuggerNonUserCode] // avoid "Exception User-Unhandled" Visual Studio messages
|
||||
public static bool ValidateCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
public bool ValidateCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
if (serverConfig.RuntimeSettings.IgnoreSslErrors == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sender.GetType() != typeof(HttpWebRequest))
|
||||
return sslPolicyErrors == SslPolicyErrors.None;
|
||||
|
||||
@@ -51,24 +56,11 @@ namespace Jackett.Common.Utils.Clients
|
||||
|
||||
public override void SetTimeout(int seconds) => ClientTimeout = seconds;
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
|
||||
// custom handler for our own internal certificates
|
||||
if (serverConfig.RuntimeSettings.IgnoreSslErrors == true)
|
||||
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
|
||||
else
|
||||
ServicePointManager.ServerCertificateValidationCallback += ValidateCertificate;
|
||||
}
|
||||
|
||||
protected override async Task<WebResult> Run(WebRequest webRequest)
|
||||
{
|
||||
ServicePointManager.SecurityProtocol = (SecurityProtocolType)192 | (SecurityProtocolType)768 | (SecurityProtocolType)3072;
|
||||
|
||||
var cookies = new CookieContainer
|
||||
{
|
||||
PerDomainCapacity = 100 // By default only 20 cookies are allowed per domain
|
||||
PerDomainCapacity = 100 // By default, only 20 cookies are allowed per domain
|
||||
};
|
||||
if (!string.IsNullOrWhiteSpace(webRequest.Cookies))
|
||||
{
|
||||
@@ -86,14 +78,17 @@ namespace Jackett.Common.Utils.Clients
|
||||
clearanceHandlr.ProxyUrl = serverConfig.GetProxyUrl(false);
|
||||
clearanceHandlr.ProxyUsername = serverConfig.ProxyUsername;
|
||||
clearanceHandlr.ProxyPassword = serverConfig.ProxyPassword;
|
||||
|
||||
using (var clientHandlr = new HttpClientHandler
|
||||
{
|
||||
CookieContainer = cookies,
|
||||
AllowAutoRedirect = false, // Do not use this - Bugs ahoy! Lost cookies and more.
|
||||
UseCookies = true,
|
||||
Proxy = webProxy,
|
||||
UseProxy = (webProxy != null),
|
||||
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
|
||||
UseProxy = webProxy != null,
|
||||
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
|
||||
MaxConnectionsPerServer = 20,
|
||||
ServerCertificateCustomValidationCallback = ValidateCertificate,
|
||||
})
|
||||
{
|
||||
clearanceHandlr.InnerHandler = clientHandlr;
|
||||
|
||||
@@ -33,14 +33,19 @@ namespace Jackett.Common.Utils.Clients
|
||||
{
|
||||
cookies = new CookieContainer
|
||||
{
|
||||
PerDomainCapacity = 100 // By default only 20 cookies are allowed per domain
|
||||
PerDomainCapacity = 100 // By default, only 20 cookies are allowed per domain
|
||||
};
|
||||
CreateClient();
|
||||
}
|
||||
|
||||
[DebuggerNonUserCode] // avoid "Exception User-Unhandled" Visual Studio messages
|
||||
public static bool ValidateCertificate(HttpRequestMessage request, X509Certificate2 certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
public bool ValidateCertificate(HttpRequestMessage request, X509Certificate2 certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
if (serverConfig.RuntimeSettings.IgnoreSslErrors == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var hash = certificate.GetCertHashString();
|
||||
|
||||
trustedCertificates.TryGetValue(hash, out var hosts);
|
||||
@@ -70,16 +75,12 @@ namespace Jackett.Common.Utils.Clients
|
||||
AllowAutoRedirect = false, // Do not use this - Bugs ahoy! Lost cookies and more.
|
||||
UseCookies = true,
|
||||
Proxy = webProxy,
|
||||
UseProxy = (webProxy != null),
|
||||
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
|
||||
UseProxy = webProxy != null,
|
||||
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
|
||||
MaxConnectionsPerServer = 20,
|
||||
ServerCertificateCustomValidationCallback = ValidateCertificate,
|
||||
};
|
||||
|
||||
// custom certificate validation handler (netcore version)
|
||||
if (serverConfig.RuntimeSettings.IgnoreSslErrors == true)
|
||||
clientHandlr.ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
|
||||
else
|
||||
clientHandlr.ServerCertificateCustomValidationCallback = ValidateCertificate;
|
||||
|
||||
clearanceHandlr.InnerHandler = clientHandlr;
|
||||
client = new HttpClient(clearanceHandlr);
|
||||
|
||||
@@ -103,13 +104,6 @@ namespace Jackett.Common.Utils.Clients
|
||||
client.Timeout = TimeSpan.FromSeconds(ClientTimeout);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
|
||||
ServicePointManager.SecurityProtocol = (SecurityProtocolType)192 | (SecurityProtocolType)768 | (SecurityProtocolType)3072;
|
||||
}
|
||||
|
||||
protected override async Task<WebResult> Run(WebRequest webRequest)
|
||||
{
|
||||
var request = new HttpRequestMessage();
|
||||
|
||||
@@ -220,11 +220,7 @@ namespace Jackett.Common.Utils.Clients
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
protected virtual async Task<WebResult> Run(WebRequest webRequest) => throw new NotImplementedException();
|
||||
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
|
||||
public virtual void Init() => ServicePointManager.DefaultConnectionLimit = 1000;
|
||||
protected virtual Task<WebResult> Run(WebRequest webRequest) => throw new NotImplementedException();
|
||||
|
||||
public virtual void OnCompleted() => throw new NotImplementedException();
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Principal;
|
||||
using Jackett.Common.Utils.Clients;
|
||||
|
||||
@@ -75,7 +76,11 @@ namespace Jackett.Common.Utils
|
||||
|
||||
public static bool IsUserAdministrator()
|
||||
{
|
||||
//bool value to hold our return value
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isAdmin;
|
||||
try
|
||||
{
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Jackett.Common.Utils
|
||||
{
|
||||
public static class StringCipher
|
||||
{
|
||||
// This constant is used to determine the keysize of the encryption algorithm in bits.
|
||||
// We divide this by 8 within the code below to get the equivalent number of bytes.
|
||||
private const int Keysize = 256;
|
||||
|
||||
// This constant determines the number of iterations for the password bytes generation function.
|
||||
private const int DerivationIterations = 1000;
|
||||
|
||||
public static string Encrypt(string plainText, string passPhrase)
|
||||
{
|
||||
// Salt and IV is randomly generated each time, but is preprended to encrypted cipher text
|
||||
// so that the same Salt and IV values can be used when decrypting.
|
||||
var saltStringBytes = Generate256BitsOfRandomEntropy();
|
||||
var ivStringBytes = Generate256BitsOfRandomEntropy();
|
||||
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
|
||||
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
|
||||
{
|
||||
var keyBytes = password.GetBytes(Keysize / 8);
|
||||
using (var symmetricKey = new RijndaelManaged())
|
||||
{
|
||||
symmetricKey.BlockSize = 256;
|
||||
symmetricKey.Mode = CipherMode.CBC;
|
||||
symmetricKey.Padding = PaddingMode.PKCS7;
|
||||
using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
|
||||
{
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
|
||||
cryptoStream.FlushFinalBlock();
|
||||
// Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.
|
||||
var cipherTextBytes = saltStringBytes;
|
||||
cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
|
||||
cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
|
||||
memoryStream.Close();
|
||||
cryptoStream.Close();
|
||||
return Convert.ToBase64String(cipherTextBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string Decrypt(string cipherText, string passPhrase)
|
||||
{
|
||||
// Get the complete stream of bytes that represent:
|
||||
// [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]
|
||||
var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
|
||||
// Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.
|
||||
var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
|
||||
// Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.
|
||||
var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
|
||||
// Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
|
||||
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray();
|
||||
|
||||
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
|
||||
{
|
||||
var keyBytes = password.GetBytes(Keysize / 8);
|
||||
using (var symmetricKey = new RijndaelManaged())
|
||||
{
|
||||
symmetricKey.BlockSize = 256;
|
||||
symmetricKey.Mode = CipherMode.CBC;
|
||||
symmetricKey.Padding = PaddingMode.PKCS7;
|
||||
using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
|
||||
{
|
||||
using (var memoryStream = new MemoryStream(cipherTextBytes))
|
||||
{
|
||||
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
|
||||
{
|
||||
var plainTextBytes = new byte[cipherTextBytes.Length];
|
||||
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
|
||||
memoryStream.Close();
|
||||
cryptoStream.Close();
|
||||
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] Generate256BitsOfRandomEntropy()
|
||||
{
|
||||
var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits.
|
||||
using (var rngCsp = new RNGCryptoServiceProvider())
|
||||
{
|
||||
// Fill the array with cryptographically secure random bytes.
|
||||
rngCsp.GetBytes(randomBytes);
|
||||
}
|
||||
return randomBytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,16 +189,11 @@ namespace Jackett.Common.Utils
|
||||
{
|
||||
var chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
var randBytes = new byte[length];
|
||||
using (var rngCsp = new RNGCryptoServiceProvider())
|
||||
{
|
||||
rngCsp.GetBytes(randBytes);
|
||||
var key = "";
|
||||
foreach (var b in randBytes)
|
||||
{
|
||||
key += chars[b % chars.Length];
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
using var rngCsp = RandomNumberGenerator.Create();
|
||||
rngCsp.GetBytes(randBytes);
|
||||
|
||||
return randBytes.Aggregate(string.Empty, (current, b) => current + chars[b % chars.Length]);
|
||||
}
|
||||
|
||||
public static IEnumerable<int> AllIndexesOf(this string source, char value)
|
||||
|
||||
@@ -29,9 +29,7 @@ namespace Jackett.Server.Services
|
||||
return null;
|
||||
|
||||
var ue = new UnicodeEncoding();
|
||||
#pragma warning disable SYSLIB0021
|
||||
var hashString = new SHA512Managed();
|
||||
#pragma warning restore SYSLIB0021
|
||||
var hashString = SHA512.Create();
|
||||
|
||||
// Append key as salt
|
||||
input += _serverConfig.APIKey;
|
||||
|
||||
@@ -341,7 +341,6 @@ namespace Jackett.Server.Services
|
||||
|
||||
// Load indexers
|
||||
indexerService.InitIndexers(configService.GetCardigannDefinitionsFolders());
|
||||
client.Init();
|
||||
|
||||
updater.CleanupTempDir();
|
||||
updater.CheckUpdaterLock();
|
||||
|
||||
@@ -48,9 +48,5 @@ namespace Jackett.Test.TestHelpers
|
||||
});
|
||||
throw new Exception($"You have to mock the URL {request.Url} with RegisterRequestCallback");
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using CommandLine;
|
||||
using CommandLine.Text;
|
||||
using Jackett.Common.Models.Config;
|
||||
@@ -15,7 +16,6 @@ namespace Jackett.Updater
|
||||
public class Program
|
||||
{
|
||||
private IProcessService processService;
|
||||
private IServiceConfigService windowsService;
|
||||
public static Logger logger;
|
||||
private Variants.JackettVariant variant = Variants.JackettVariant.NotFound;
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Jackett.Updater
|
||||
|
||||
private void Run(string[] args)
|
||||
{
|
||||
var runtimeSettings = new RuntimeSettings()
|
||||
var runtimeSettings = new RuntimeSettings
|
||||
{
|
||||
CustomLogFileName = "updater.txt"
|
||||
};
|
||||
@@ -42,7 +42,8 @@ namespace Jackett.Updater
|
||||
variant = variants.GetVariant();
|
||||
logger.Info("Jackett variant: " + variant.ToString());
|
||||
|
||||
var isWindows = Environment.OSVersion.Platform == PlatformID.Win32NT;
|
||||
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||
|
||||
if (isWindows)
|
||||
{
|
||||
//The updater starts before Jackett closes
|
||||
@@ -51,18 +52,13 @@ namespace Jackett.Updater
|
||||
}
|
||||
|
||||
processService = new ProcessService(logger);
|
||||
windowsService = new WindowsServiceConfigService(processService, logger);
|
||||
|
||||
var commandLineParser = new Parser(settings => settings.CaseSensitive = false);
|
||||
|
||||
try
|
||||
{
|
||||
var optionsResult = commandLineParser.ParseArguments<UpdaterConsoleOptions>(args);
|
||||
optionsResult.WithParsed(options =>
|
||||
{
|
||||
ProcessUpdate(options);
|
||||
}
|
||||
);
|
||||
optionsResult.WithParsed(ProcessUpdate);
|
||||
optionsResult.WithNotParsed(errors =>
|
||||
{
|
||||
logger.Error(HelpText.AutoBuild(optionsResult));
|
||||
@@ -135,10 +131,10 @@ namespace Jackett.Updater
|
||||
if (options.KillPids != null)
|
||||
{
|
||||
var pidsStr = options.KillPids.Split(',').Where(pid => !string.IsNullOrWhiteSpace(pid)).ToArray();
|
||||
pids = Array.ConvertAll(pidsStr, pid => int.Parse(pid));
|
||||
pids = Array.ConvertAll(pidsStr, int.Parse);
|
||||
}
|
||||
|
||||
var isWindows = Environment.OSVersion.Platform == PlatformID.Win32NT;
|
||||
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||
var trayRunning = false;
|
||||
var trayProcesses = Process.GetProcessesByName("JackettTray");
|
||||
if (isWindows)
|
||||
@@ -897,7 +893,9 @@ namespace Jackett.Updater
|
||||
|
||||
// kill pids after the update on UNIX
|
||||
if (!isWindows)
|
||||
{
|
||||
KillPids(pids);
|
||||
}
|
||||
|
||||
if (!options.NoRestart)
|
||||
{
|
||||
@@ -913,6 +911,8 @@ namespace Jackett.Updater
|
||||
logger.Info("Starting Tray: " + startInfo.FileName + " " + startInfo.Arguments);
|
||||
Process.Start(startInfo);
|
||||
|
||||
var windowsService = new WindowsServiceConfigService(processService, logger);
|
||||
|
||||
if (!windowsService.ServiceExists())
|
||||
{
|
||||
//User was running the tray icon, but not using the Windows service, starting Tray icon will start JackettConsole as well
|
||||
@@ -920,10 +920,12 @@ namespace Jackett.Updater
|
||||
}
|
||||
}
|
||||
|
||||
if (string.Equals(options.Type, "WindowsService", StringComparison.OrdinalIgnoreCase))
|
||||
if (isWindows && string.Equals(options.Type, "WindowsService", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
logger.Info("Starting Windows service");
|
||||
|
||||
var windowsService = new WindowsServiceConfigService(processService, logger);
|
||||
|
||||
try
|
||||
{
|
||||
windowsService.Start();
|
||||
|
||||
Reference in New Issue
Block a user