Compare commits

...

142 Commits

Author SHA1 Message Date
Qstick
fa304dcaca Random 2022-01-02 23:39:55 -06:00
Qstick
e04133d34a More Mono Cleaning 2022-01-02 23:19:47 -06:00
Qstick
07575ae239 Fixed: (LazyLibrarian) Test indexer pull on setup to validate 2022-01-02 21:50:44 -06:00
Qstick
8e7acd8946 Fixed: (Mylar) Test indexer pull on setup to validate Mylar functionality 2022-01-02 21:50:44 -06:00
Qstick
3ecc926298 Fixed: (HDTorrents) Use Sanitized search string on all search types 2022-01-02 19:43:46 -06:00
Qstick
1e532624af Fixed: (Redacted) Guid and FL Parsing in line with Gazelle 2022-01-02 16:45:34 -06:00
Agneev Mukherjee
8a5194e604 Update index.ejs 2022-01-02 13:10:07 -06:00
Agneev Mukherjee
8a73cf72c2 Set login.html theme-color to color of logo 2022-01-02 13:10:07 -06:00
Qstick
76982c5988 Fixed: (Gazelle) Freeleech detection for releases 2022-01-01 20:08:59 -06:00
Qstick
b9dfe5e359 Fixed: (Gazelle) Use InfoUrl for GUID to avoid global duplicates 2022-01-01 20:01:40 -06:00
Qstick
a5e13ca776 New: Genre parameter for Music search 2022-01-01 15:04:05 -06:00
Qstick
e2ddfbff9c New: Genre parameter for Movie search 2022-01-01 14:49:01 -06:00
Qstick
66b4c7891d New: TmdbId Parameter for TV Search 2022-01-01 14:43:49 -06:00
Qstick
480a76c290 New: Support for language metadata 2022-01-01 14:23:44 -06:00
Servarr
1373ab255d Automated API Docs update 2021-12-31 19:41:04 -06:00
Qstick
1dc00eb445 More powerful label actions 2021-12-31 19:34:28 -06:00
Qstick
a366bec684 New: Reenable TV Id search for PrivateHD 2021-12-31 19:34:28 -06:00
Qstick
ecca6e9f49 New: TVDB and TMDB Search for AvistaZ
Ref #717
2021-12-31 19:34:13 -06:00
Yukine
03db7a9bbd Fixed: (SpeedApp) correct categories in query string of requests 2021-12-31 15:09:03 -06:00
Qstick
9cb04466c1 DbType param on update check 2021-12-31 13:19:22 -06:00
bakerboy448
2bae37d0c5 Fixed: (TorrentLeech) Calculating Incorrect Age
Closes #720
2021-12-31 11:04:31 -06:00
bakerboy448
0dbd23c52b Fixed: Various Translations 2021-12-30 23:34:48 -06:00
bakerboy448
66a6311dcc Fixed: SemiPublic => SemiPrivate 2021-12-30 23:00:19 -06:00
Servarr
c5b111530c Automated API Docs update 2021-12-30 22:37:20 -06:00
Qstick
77724a50a4 Really fix Github token for API PR 2021-12-30 21:42:46 -06:00
Qstick
22cbd01c57 Bump to 0.1.10 2021-12-30 19:03:04 -06:00
Yukine
fd55a624a7 Fixed: (AnimeBytes) Do not Page requests 2021-12-30 19:01:42 -06:00
Qstick
75984e954e Update LocalizationController.cs 2021-12-30 18:41:56 -06:00
Qstick
3fce120578 Fixed: (SpeedApp) Map Categories instead of building
Fixes #574
2021-12-30 18:30:58 -06:00
Qstick
6e8fb22c71 New: Additional logging for InvalidModel BadRequest API calls
[common]
2021-12-30 17:51:34 -06:00
Qstick
8ec7a4898d Maintain PrimaryKey and AutoIncrement on some schemas
[common]
2021-12-30 17:51:11 -06:00
Qstick
642848d331 API Annotations 2021-12-30 15:51:51 -06:00
Qstick
c9e6a0339e Fixed: (Cardigann) Indexer privacy tweaks, Semi-Public fixes
Fixes #744
2021-12-29 18:18:09 -06:00
Qstick
25620e8670 Fix GitHub token variable usage 2021-12-29 18:16:36 -06:00
Mouton99
5b804e8f3a New: (TorrentSeeds) Migrate to API & YML 2021-12-29 18:14:22 -06:00
Servarr
548db6a5cd Automated API Docs update 2021-12-29 17:48:53 -06:00
Qstick
7f28f64cbe Fix server settings on API docs 2021-12-28 18:47:44 -06:00
Qstick
9bad31af84 Eliminate PR Extension from pipeline 2021-12-28 18:26:51 -06:00
Qstick
01c7a05841 Only push to api-docs when changes 2021-12-28 17:52:15 -06:00
Qstick
9859b4a3d9 force it 2021-12-28 17:37:30 -06:00
Gabriel Sjöberg
177084fe8b Fixed: (Indexer) Update RARBG API query options
* Added app_id to captcha check to avoid 403 forbidden error
* Migrated app_id from hard coded to BuildInfo.AppName
2021-12-28 17:04:55 -06:00
Qstick
c57a91bc64 Skip build of doc only change, ignore PR errors for docs 2021-12-28 17:01:31 -06:00
Servarr
ca67a40c72 Automated API Docs update 2021-12-28 15:39:14 -06:00
Qstick
de7505bbe6 correctly push upstream 2021-12-28 15:27:54 -06:00
Qstick
97956ce951 Branch and push prior to PR 2021-12-28 15:10:56 -06:00
Qstick
8a38e124fd Speed up Checkout for Docs job 2021-12-28 14:45:28 -06:00
Qstick
38fcffe871 Identify user for git 2021-12-28 14:31:12 -06:00
Qstick
4c7b5a47d3 Autogenerated API docs 2021-12-28 13:43:45 -06:00
Qstick
34597e6ecb Boolean default should be a boolean
Fixes #729
2021-12-24 14:23:09 -06:00
bakerboy448
735be4f467 New: (TvVault) Mark as Obsolete per Site Bot Ban
Closes #573
2021-12-24 14:08:00 -06:00
bakerboy448
1c737d77fb Bump to 0.1.9 2021-12-24 14:07:42 -06:00
bakerboy448
55788ac04d Fixed: (Usenet) (DrunkenSlug) Update URL
https://api.drunkenslug.com gives a 301
2021-12-24 14:07:17 -06:00
bakerboy448
d108ab0339 Fix misleading Tags helptext [skip ci] 2021-12-24 14:06:42 -06:00
bakerboy448
5928eea83e Fixed: (PornoLab) Add new 2022 Categories
Based on Jackett f61a2b47400e68422ba6620a5ef2f5b4d0a929a3
2021-12-24 12:58:51 -06:00
bakerboy448
27898aa3b5 Fixed: (DanishBytes) Update Domain to .club
Based on Jackett f890ddd119a35eb4ba40b407aa65461c713b5e5d
2021-12-24 12:58:51 -06:00
bakerboy448
5e3322c538 New: OnApplicationUpdate Notifications
(based on Radarr Commits
9e175e28efcfc6ac3e414649b955a10fb0e951e7
4f5f9ff77ee4de05ba04cc677eb7baf4df726af5
4ebcbc28aa3e3268ecc37c5fc2b5565da8f13305
)
Fixes #723

Co-authored-by: Qstick <qstick@gmail.com>
2021-12-22 19:07:07 -06:00
bakerboy448
80c31e8660 fixup add rationale for Obsolete of C# Indexer Implementations 2021-12-18 09:34:51 -06:00
bakerboy448
46401ee187 Fixed: (BB) Remove '.' from Search String
based on jackett fbb1f15d7014b2d8c23c6ee94c2bcf37612066db
2021-12-18 09:27:26 -06:00
Davo1624
3610becc64 Fixed: (Orpheus) Drop Caps Support for Movie & TV Search
Drop TV and Movie search as no Movie/TV categories (#713)

e-learning results are how to play guitar videos, etc
comedy are comedy/standup recordings
2021-12-15 16:30:26 -06:00
Davo1624
06d9c157d8 Fixed: (Orpheus) Map Categories Comedy & E-Learning Videos to 'Other'
indexer does not actually have movies and tv
2021-12-15 16:27:50 -06:00
bakerboy448
d0d1f40128 Fixed: (Anilibria) Duplicate entries
Mark C# Anilibria as obsolete
2021-12-12 17:28:07 -06:00
bakerboy448
383d5464b7 New: (FlareSolverr Proxy) Configurable Request Timeout
Closes #696
2021-12-11 22:10:06 -06:00
Qstick
62d15536df Fixed: NullRef in SchemaBuilder when sending payload without optional Provider.Settings fields 2021-12-11 13:38:25 -06:00
ta264
147cdf2cce Fixed: Forms login persists across restarts in docker
(cherry picked from commit a219b4a1b869863b2ef47d4bdf33d308cb261ba3)

Fixes #409
2021-12-10 03:14:55 -06:00
bakerboy448
dd27d69e97 fix erroneous logging for windows service on non-windows 2021-12-10 03:14:23 -06:00
Robin Dadswell
32fd0911a2 New: Application Placeholders instead of default values 2021-12-09 15:19:11 -06:00
Robin Dadswell
0e6ec58a83 New: Placeholders in notification fields 2021-12-09 15:19:11 -06:00
Robin Dadswell
69f5963f6f New: Frontend Placeholders from the Backend 2021-12-09 15:19:11 -06:00
PearsonFlyer
6ca708f523 Fixed: (HDTorrents) Remove . from searches 2021-12-08 14:26:46 -06:00
ta264
9e7af8369e Fixed: Support older glibc in libMonoPosixHelper 2021-12-08 19:14:25 +00:00
Qstick
b05d8c930d Date Routines Test Cases 2021-12-07 20:19:05 -06:00
Qstick
6b886b938c New: Better Fuzzy DateTime Parse 2021-12-07 18:48:46 -06:00
ta264
4a7bf39723 Fixed: Speed up parsing DateTime 2021-12-06 21:57:22 +00:00
bakerboy448
7fcd320e23 Fixed: (PrivateHD) Drop support for IMDB search
imdb search does not support S/E params
this matches the behavior for TorrentLeech
Fixes #119
2021-12-06 21:12:37 -06:00
Qstick
88677ce236 Bump to 0.1.8 2021-12-06 17:19:04 -06:00
Qstick
d2cf060473 Fixed: (LazyLibrarian) Use listNabProviders instead of listProviders 2021-12-06 16:37:16 -06:00
Qstick
3b7b72d4e1 Fixed: (Cardigann) Always use search headers for download if defined 2021-12-05 17:43:15 -06:00
Qstick
4e69b80a98 Bump to 0.1.7 2021-12-05 17:29:22 -06:00
Qstick
0f52258d53 Fixed: (Flaresolverr) YggCookie and YggTorrent Issues 2021-12-05 17:26:58 -06:00
Qstick
4eadd4cb2f New: LazyLibrarian Sync Support
Closes #469

Co-Authored-By: philborman <12158777+philborman@users.noreply.github.com>
2021-12-05 11:41:51 -06:00
Qstick
579b8a3d3b New: (Cardigann) More feed metadata for book and music 2021-12-05 11:23:47 -06:00
bakerboy448
849b3de7d3 readme updates [skip ci] 2021-12-05 13:21:50 +00:00
Robin Dadswell
8855b2846d Fixed: Updated wording of Application Server URLs 2021-12-05 07:21:15 -06:00
bakerboy448
c64addb976 Fixed: (UNIT3D Indexers) Cleanse RID in Logs
Fixes #652
2021-12-04 18:51:33 -06:00
Qstick
fab1304bcd Skip DB backup on Postgres DB 2021-12-04 18:50:33 -06:00
Qstick
bd834fb4d7 Fixed: Stats fails to load due to unparsable elapsedTime for history event
Fixes #663
2021-12-04 18:03:33 -06:00
Qstick
dcee9582bd Speedup Stats endpoint call X3 2021-12-04 17:24:20 -06:00
Qstick
89e500edfd Fixed: (Stats) All filter not returning all 2021-12-04 17:18:20 -06:00
Qstick
ea83020714 Build Magnet on Cardigann separate 2021-12-04 17:17:47 -06:00
Qstick
6d62744667 Fixed: (Cardigann) Magnet generation for public indexers with InfoHash
Fixes #668
2021-12-04 12:26:55 -06:00
Qstick
08c68e26c1 Fixed: Correctly return infohash in torznab response when available 2021-12-04 12:10:19 -06:00
Qstick
574568e71d Optimize HandleJsonSelector() to avoid needless throws 2021-12-04 12:09:23 -06:00
Qstick
c83c818380 Fixed: (Flaresolverr) Ignore http errors on initial request when using FS 2021-12-01 21:50:28 -06:00
Qstick
a2df38b1ca Fixed: Windows installer and adding/removing services
Co-Authored-By: ta264 <ta264@users.noreply.github.com>
2021-12-01 21:46:33 -06:00
Qstick
89510c4a65 Fixed: Workaround net6 object serialization issues
Co-Authored-By: ta264 <ta264@users.noreply.github.com>
2021-12-01 20:39:26 -06:00
Qstick
b5a2f68bde Fixed: (Newznab) Parse Imdb, Tmdb, RageId, Tvdbid from indexer
Fixes #656
2021-11-30 21:47:44 -06:00
Qstick
1ffab661da Don't fallback to different OS Yarn cache 2021-11-30 11:20:15 -06:00
Qstick
bf0a627a4e New: (Indexer) PornoLab 2021-11-30 07:56:47 -06:00
Qstick
df764ce8b4 New: Postgres Support 2021-11-29 23:14:48 -06:00
Qstick
a61d4ab88c New: Stats filters 2021-11-29 23:14:48 -06:00
Qstick
01e7e924c4 Fixed: (Flaresolverr) Proxy Test
Fixes #651
2021-11-29 21:00:38 -06:00
Qstick
5f5df99dab Improved logging on bad date parse 2021-11-29 21:00:11 -06:00
bakerboy448
77e40e8e53 Fixed: (TorrentSyndikat) Download URL missing API 2021-11-29 19:36:47 -06:00
Qstick
d3853c1a54 Bump version to 0.1.6 2021-11-28 22:15:09 -06:00
ta264
02ecc04526 Fixed: Restarting windows service from UI 2021-11-28 16:51:42 -06:00
Qstick
2e103d6dba ParseUtil Cleanup 2021-11-28 16:47:16 -06:00
Qstick
9b9d2f2798 Fixed: Tray app restart
Co-Authored-By: ta264 <ta264@users.noreply.github.com>
2021-11-28 16:46:15 -06:00
Qstick
b80bfaeff0 Bump System.Data.SQLite.Core.Servar 2021-11-28 15:03:23 -06:00
bakerboy448
80b6821e46 Fixed: (Xthor) Details Linking to API and not the site
Fixes #577
2021-11-28 14:33:46 -06:00
bakerboy448
dbf81a7c9e Fixed: Outdated Definition Healthcheck not using Definition Name 2021-11-28 11:12:08 -06:00
Qstick
cb2e5953d5 Bump version to 0.1.5 2021-11-28 01:57:32 -06:00
bakerboy448
f5d6fa9138 Fixed: (Cardigann) Blocklist depreciated definitions
reverts 1abd14ee86
adds additional v3 definitions
2021-11-27 23:34:29 -06:00
Qstick
7ce5df63d4 Fixed: (Cardigann) Number normalize to ignore non-digits 2021-11-27 22:38:52 -06:00
Qstick
10aa3e43a2 Fixed: (Cardigann) Use correct encoding for Auth and Download calls 2021-11-27 21:46:55 -06:00
Qstick
387f8df0ff Rework some Cardigann exception handling 2021-11-27 21:07:42 -06:00
Qstick
d61ce3f27c Fixed: (Cardigann) Fails when Int variables used in requests 2021-11-27 20:50:58 -06:00
bakerboy448
eff8fdebf5 cleanse postgres username/pass creds
Fixes #632
2021-11-26 18:31:57 -06:00
bakerboy448
01fe990417 Update Github Templates [skip ci]
(cherry picked from commit beb22844c960db8e0f42a32809ad74cffce06e09)
2021-11-26 12:15:01 -06:00
bakerboy448
60f37de6af Fixed: (Orpheus) Try to actually use the token 2021-11-25 16:51:17 -06:00
Weblate
cda373f195 Translated using Weblate (Finnish)
Currently translated at 99.5% (445 of 447 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (447 of 447 strings)

Translated using Weblate (Finnish)

Currently translated at 99.5% (445 of 447 strings)

Translated using Weblate (German)

Currently translated at 95.3% (426 of 447 strings)

Co-authored-by: Csaba <csab0825@gmail.com>
Co-authored-by: Don-Chris <Chr_Sch@t-online.de>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/hu/
Translation: Servarr/Prowlarr
2021-11-25 10:29:24 -06:00
Qstick
510ccf4bce New: Set GUID in IndexerBase if Indexer doesn't set it explicitly 2021-11-25 09:10:16 -06:00
Qstick
0fd411a37f Fixed: Default Cardigann to DownloadUrl GUID 2021-11-25 09:08:51 -06:00
Qstick
cd7c73bad3 Fixed: Indexer not removed in UI when deleted
Indexer doesn't get removed because createAjaxReqest expects JSON type return, however after net6 provider delete event returns text/html type.
2021-11-23 18:18:00 -06:00
bakerboy448
44f12d9569 Fixed: (RarBG) Do not cleanse get_token 2021-11-23 14:57:03 -06:00
Qstick
51f8b1b5c4 Fixed: (Nebulance) Handle null poster 2021-11-23 10:17:23 -06:00
bakerboy448
8bce43ac29 Fixed: (Cardigann) Poster & tmbid are Optional by default
missed jackett commit f5802306fa1fe4edb4c7f997100a6caebe71e989
Fixes #629
2021-11-23 10:11:38 -06:00
Qstick
884aecf846 New: Store call URL in History, Link in UI 2021-11-22 17:14:52 -06:00
Qstick
1183a0386d New: Grab Title column option in History
Fixes #628
2021-11-22 16:40:06 -06:00
Qstick
5979c5f8fe Cleanup Config Options
Fixes #610
2021-11-22 16:40:05 -06:00
Qstick
25bb5b6427 Bump React-dnd to 14.0.4 2021-11-22 16:40:05 -06:00
Qstick
4d021fe8dc Fixed: Default search type should be 'search' 2021-11-22 08:09:32 -06:00
Qstick
520d82ed20 lint fixup 2021-11-21 18:58:02 -06:00
Weblate
f19a81990b Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (447 of 447 strings)

Translated using Weblate (Finnish)

Currently translated at 100.0% (447 of 447 strings)

Translated using Weblate (Finnish)

Currently translated at 100.0% (441 of 441 strings)

Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pt_BR/
Translation: Servarr/Prowlarr
2021-11-21 18:48:46 -06:00
Qstick
03cde56333 New: Additional logging for requests when query/grab limit are set 2021-11-21 18:46:12 -06:00
Qstick
19862cda6c Bump Webpack plugins and loaders 2021-11-21 18:32:39 -06:00
Qstick
230a0e5aa0 Bump Sentry to 6.15.0 2021-11-21 18:03:07 -06:00
Qstick
4273f2d6f3 Bump Webpack to 5.64.2 2021-11-21 18:00:02 -06:00
Qstick
a4c92fc629 Update lint packages 2021-11-21 17:57:24 -06:00
Qstick
ce901ae8a3 Update translate.js to use createAjaxRequest 2021-11-21 17:46:24 -06:00
Qstick
5d32bcf8b9 New: Bulk Grab Releases and Parameter Search 2021-11-20 18:00:55 -06:00
Chris Sandvik
f69f96695b New: Add a filter bar to the "Add Indexers" modal (#607)
* Add a filter bar to the "Add Indexers" modal

* Fix stylelint errors

* Hide AddIndexerModal alert on small screens
2021-11-20 11:31:45 -06:00
Qstick
77fa113d77 Bump version to 0.1.4 2021-11-20 03:27:00 -06:00
411 changed files with 7660 additions and 4942 deletions

View File

@@ -63,12 +63,11 @@ body:
required: true
- type: textarea
attributes:
label: Anything else?
label: Trace Logs?
description: |
Trace Logs (https://wiki.servarr.com/prowlarr/troubleshooting#logging-and-log-files)
Links? References? Anything that will give us more context about the issue you are encountering!
***Generally speaking, all bug reports must have trace logs provided.***
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
Additionally, any additional info? Screenshots? References? Anything that will give us more context about the issue you are encountering!
validations:
required: true

16
.github/label-actions.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
# Configuration for Label Actions - https://github.com/dessant/label-actions
'Type: Support':
comment: >
:wave: @{issue-author}, we use the issue tracker exclusively
for bug reports and feature requests. However, this issue appears
to be a support request. Please hop over onto our [Discord](https://prowlarr.com/discord)
or [Subreddit](https://reddit.com/r/prowlarr)
close: true
'Type: Indexer Request':
comment: >
:wave: @{issue-author}, we use the issue tracker exclusively
for bug reports and feature requests. However, this issue appears
to be a indexer request. Please use our Indexer request [site](https://requests.prowlarr.com/)
close: true

23
.github/workflows/label-actions.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: 'Label Actions'
on:
issues:
types: [labeled, unlabeled]
pull_request:
types: [labeled, unlabeled]
discussion:
types: [labeled, unlabeled]
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
jobs:
action:
runs-on: ubuntu-latest
steps:
- uses: dessant/label-actions@v2
with:
process-only: 'issues, prs'

View File

@@ -1,21 +0,0 @@
name: 'Support requests'
on:
issues:
types: [labeled, unlabeled, reopened]
jobs:
support:
runs-on: ubuntu-latest
steps:
- uses: dessant/support-requests@v2
with:
github-token: ${{ github.token }}
support-label: 'Type: Support'
issue-comment: >
:wave: @{issue-author}, we use the issue tracker exclusively
for bug reports and feature requests. However, this issue appears
to be a support request. Please hop over onto our [Discord](https://prowlarr.com/discord)
or [Subreddit](https://reddit.com/r/prowlarr)
close-issue: true
lock-issue: false

4
.gitignore vendored
View File

@@ -188,6 +188,10 @@ packages.config.md5sum
**/.idea/**/*.iml
**/.idea/**/contentModel.xml
**/.idea/**/modules.xml
# ignore node_modules symlink
node_modules
node_modules.nosync
# API doc generation
.config/

View File

@@ -4,74 +4,84 @@
[![Translated](https://translate.servarr.com/widgets/servarr/-/prowlarr/svg-badge.svg)](https://translate.servarr.com/engage/prowlarr/?utm_source=widget)
[![Docker Pulls](https://img.shields.io/docker/pulls/hotio/prowlarr.svg)](https://wiki.servarr.com/prowlarr/installation#docker)
![Github Downloads](https://img.shields.io/github/downloads/Prowlarr/Prowlarr/total.svg)
[![Backers on Open Collective](https://opencollective.com/Prowlarr/backers/badge.svg)](#backers)
[![Backers on Open Collective](https://opencollective.com/Prowlarr/backers/badge.svg)](#backers)
[![Sponsors on Open Collective](https://opencollective.com/Prowlarr/sponsors/badge.svg)](#sponsors)
[![Mega Sponsors on Open Collective](https://opencollective.com/Prowlarr/megasponsors/badge.svg)](#mega-sponsors)
Prowlarr is an indexer manager/proxy built on the popular arr .net/reactjs base stack to integrate with your various PVR apps. Prowlarr supports management of both Torrent Trackers and Usenet Indexers. It integrates seamlessly with Lidarr, Mylar3, Radarr, Readarr, and Sonarr offering complete management of your indexers with no per app Indexer setup required (we do it all).
Prowlarr is an indexer manager/proxy built on the popular \*arr .net/reactjs base stack to integrate with your various PVR apps. Prowlarr supports management of both Torrent Trackers and Usenet Indexers. It integrates seamlessly with Lidarr, Mylar3, Radarr, Readarr, and Sonarr offering complete management of your indexers with no per app Indexer setup required (we do it all).
## Major Features Include:
- Usenet support for 24 indexers natively, including Headphones VIP, and support for any Newznab compatible indexer via "Generic Newznab"
## Major Features Include
- Usenet support for 24 indexers natively, including Headphones VIP
- Usenet support for any Newznab compatible indexer via "Generic Newznab"
- Torrent support for over 500 trackers with more added all the time
- Torrent support for any Torznab compatible tracker via "Generic Torznab"
- Indexer Sync to Sonarr/Radarr/Readarr/Lidarr/Mylar3, so no manual configuration of the other applications are required
- Support for custom YML definitions via Cardigann that includes JSON and XML parsing
- Indexer Sync to Lidarr/Mylar3/Radarr/Readarr/Sonarr, so no manual configuration of the other applications are required
- Indexer history and statistics
- Manual searching of Trackers & Indexers at a category level
- Support for pushing releases directly to your download clients from Prowlarr
- Parameter based manual searching
- Support for pushing multiple releases at once directly to your download clients from Prowlarr
- Indexer health and status notifications
- Per Indexer proxy support (SOCKS4, SOCKS5, HTTP, Flaresolverr)
## Support
Note: Prowlarr is currently early in life, thus bugs should be expected
[![Wiki](https://img.shields.io/badge/servarr-wiki-181717.svg?maxAge=60)](https://wiki.servarr.com/prowlarr)
[![Discord](https://img.shields.io/badge/discord-chat-7289DA.svg?maxAge=60)](https://prowlarr.com/discord)
[![Reddit](https://img.shields.io/badge/reddit-discussion-FF4500.svg?maxAge=60)](https://www.reddit.com/r/Prowlarr)
Note: GitHub Issues are for Bugs and Feature Requests Only
[![GitHub - Bugs and Feature Requests Only](https://img.shields.io/badge/github-issues-red.svg?maxAge=60)](https://github.com/Prowlarr/Prowlarr/issues)
[![Wiki](https://img.shields.io/badge/servarr-wiki-181717.svg?maxAge=60)](https://wiki.servarr.com/prowlarr)
## Indexers/Trackers
## Indexers & Trackers
[Supported Indexers](https://wiki.servarr.com/en/prowlarr/supported-indexers)
[![Supported Indexers](https://img.shields.io/badge/Supported%20Indexers-View%20all%20currently%20supported%20indexers%20%26%20trackers-important)](https://wiki.servarr.com/en/prowlarr/supported-indexers)
[Indexer Requests](https://requests.prowlarr.com)
- Request or vote on an existing request for a new tracker/indexer
[![Indexer Requests](https://img.shields.io/badge/Indexer%20Requests-Create%20and%20view%20existing%20requests%20for%20trackers%20and%20indexers-informational)](https://requests.prowlarr.com)
## Contributors & Developers
[API Documentation](https://prowlarr.com/docs/api/)
This project exists thanks to all the people who contribute.
- [Contribute (GitHub)](CONTRIBUTING.md)
- [Contribution (Wiki Article)](https://wiki.servarr.com/prowlarr/contributing)
- [YML Indexer Defintion (Wiki Article)](https://wiki.servarr.com/prowlarr/cardigann-yml-definition)
- [YML Indexer Definition (Wiki Article)](https://wiki.servarr.com/prowlarr/cardigann-yml-definition)
This project exists thanks to all the people who contribute.
<a href="https://github.com/Prowlarr/Prowlarr/graphs/contributors"><img src="https://opencollective.com/Prowlarr/contributors.svg?width=890&button=false" /></a>
[![Contributors List](https://opencollective.com/Prowlarr/contributors.svg?width=890&button=false)](https://github.com/Prowlarr/Prowlarr/graphs/contributors)
## Backers
Thank you to all our backers! 🙏 [Become a backer](https://opencollective.com/Prowlarr#backer)
<img src="https://opencollective.com/Prowlarr/backers.svg?width=890"></a>
![Backers List](https://opencollective.com/Prowlarr/backers.svg?width=890)
## Sponsors
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor](https://opencollective.com/Prowlarr#sponsor)
<img src="https://opencollective.com/Prowlarr/sponsors.svg?width=890"></a>
![Sponsors List](https://opencollective.com/Prowlarr/sponsors.svg?width=890)
## Mega Sponsors
<img src="https://opencollective.com/Prowlarr/tiers/mega-sponsor.svg?width=890"></a>
![Mega Sponsors List](https://opencollective.com/Prowlarr/tiers/mega-sponsor.svg?width=890)
## JetBrains
Thank you to [<img src="/Logo/jetbrains.svg" alt="JetBrains" width="32"> JetBrains](http://www.jetbrains.com/) for providing us with free licenses to their great tools.
* [<img src="/Logo/resharper.svg" alt="ReSharper" width="32"> ReSharper](http://www.jetbrains.com/resharper/)
* [<img src="/Logo/webstorm.svg" alt="WebStorm" width="32"> WebStorm](http://www.jetbrains.com/webstorm/)
* [<img src="/Logo/rider.svg" alt="Rider" width="32"> Rider](http://www.jetbrains.com/rider/)
* [<img src="/Logo/dottrace.svg" alt="dotTrace" width="32"> dotTrace](http://www.jetbrains.com/dottrace/)
- [<img src="/Logo/resharper.svg" alt="ReSharper" width="32"> ReSharper](http://www.jetbrains.com/resharper/)
- [<img src="/Logo/webstorm.svg" alt="WebStorm" width="32"> WebStorm](http://www.jetbrains.com/webstorm/)
- [<img src="/Logo/rider.svg" alt="Rider" width="32"> Rider](http://www.jetbrains.com/rider/)
- [<img src="/Logo/dottrace.svg" alt="dotTrace" width="32"> dotTrace](http://www.jetbrains.com/dottrace/)
### License
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
* Copyright 2010-2021
- [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
- Copyright 2010-2022
Icon Credit:
<a href="https://www.freepik.com/vectors/box">Box vector created by freepik - www.freepik.com</a>
Icon Credit - [Box vector created by freepik - www.freepik.com](https://www.freepik.com/vectors/box)

View File

@@ -7,7 +7,7 @@ variables:
outputFolder: './_output'
artifactsFolder: './_artifacts'
testsFolder: './_tests'
majorVersion: '0.1.3'
majorVersion: '0.1.10'
minorVersion: $[counter('minorVersion', 1)]
prowlarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(prowlarrVersion)'
@@ -29,6 +29,7 @@ pr:
paths:
exclude:
- src/NzbDrone.Core/Localization/Core
- src/Prowlarr.API.*/openapi.json
stages:
- stage: Setup
@@ -163,7 +164,6 @@ stages:
key: 'yarn | "$(osName)" | yarn.lock'
restoreKeys: |
yarn | "$(osName)"
yarn
path: $(yarnCacheFolder)
displayName: Cache Yarn packages
- bash: ./build.sh --frontend
@@ -816,7 +816,6 @@ stages:
key: 'yarn | "$(osName)" | yarn.lock'
restoreKeys: |
yarn | "$(osName)"
yarn
path: $(yarnCacheFolder)
displayName: Cache Yarn packages
- bash: ./build.sh --lint
@@ -825,6 +824,59 @@ stages:
FORCE_COLOR: 0
YARN_CACHE_FOLDER: $(yarnCacheFolder)
- job: Api_Docs
displayName: API Docs
dependsOn: Prepare
condition: |
and
(
and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')),
and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
)
pool:
vmImage: windows-2019
steps:
- task: UseDotNet@2
displayName: 'Install .net core'
inputs:
version: $(dotnetVersion)
- checkout: self
submodules: true
persistCredentials: true
fetchDepth: 1
- bash: ./docs.sh Windows
displayName: Create openapi.json
- bash: |
git config --global user.email "development@lidarr.audio"
git config --global user.name "Servarr"
git checkout -b api-docs
git add .
if git status | grep -q modified
then
git commit -am 'Automated API Docs update'
git push -f --set-upstream origin api-docs
curl -X POST -H "Authorization: token ${GITHUBTOKEN}" -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/prowlarr/prowlarr/pulls -d '{"head":"api-docs","base":"develop","title":"Update API docs"}'
else
echo "No changes since last run"
fi
displayName: Commit API Doc Change
continueOnError: true
env:
GITHUBTOKEN: $(githubToken)
- task: CopyFiles@2
displayName: 'Copy openapi.json to: $(Build.ArtifactStagingDirectory)'
inputs:
SourceFolder: '$(Build.SourcesDirectory)'
Contents: |
**/*openapi.json
TargetFolder: '$(Build.ArtifactStagingDirectory)/api_docs'
- publish: $(Build.ArtifactStagingDirectory)/api_docs
artifact: 'APIDocs'
displayName: Publish API Docs Bundle
condition: and(succeeded(), eq(variables['System.JobAttempt'], '1'))
- job: Analyze_Backend
displayName: Backend
dependsOn: Prepare

38
docs.sh Normal file
View File

@@ -0,0 +1,38 @@
PLATFORM=$1
if [ "$PLATFORM" = "Windows" ]; then
RUNTIME="win-x64"
elif [ "$PLATFORM" = "Linux" ]; then
WHERE="linux-x64"
elif [ "$PLATFORM" = "Mac" ]; then
WHERE="osx-x64"
else
echo "Platform must be provided as first arguement: Windows, Linux or Mac"
exit 1
fi
outputFolder='_output'
testPackageFolder='_tests'
rm -rf $outputFolder
rm -rf $testPackageFolder
slnFile=src/Prowlarr.sln
platform=Posix
dotnet clean $slnFile -c Debug
dotnet clean $slnFile -c Release
dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids
dotnet new tool-manifest
dotnet tool install --version 6.2.3 Swashbuckle.AspNetCore.Cli
dotnet tool run swagger tofile --output ./src/Prowlarr.Api.V1/openapi.json "$outputFolder/net6.0/$RUNTIME/prowlarr.console.dll" v1 &
sleep 10
kill %1
exit 0

View File

@@ -77,7 +77,9 @@ function AppUpdatedModalContent(props) {
<div>
{
!update.changes &&
<div className={styles.maintenance}>Maintenance release</div>
<div className={styles.maintenance}>
{translate('MaintenanceRelease')}
</div>
}
{

View File

@@ -4,7 +4,5 @@ export const CLEAR_HISTORY = 'ClearHistory';
export const CLEAR_LOGS = 'ClearLog';
export const DELETE_LOG_FILES = 'DeleteLogFiles';
export const DELETE_UPDATE_LOG_FILES = 'DeleteUpdateLogFiles';
export const INTERACTIVE_IMPORT = 'ManualImport';
export const RESET_API_KEY = 'ResetApiKey';
export const RSS_SYNC = 'RssSync';
export const APP_INDEXER_SYNC = 'ApplicationIndexerSync';

View File

@@ -70,18 +70,18 @@ class FileBrowserModalContent extends Component {
} else {
this._scrollerNode = null;
}
}
};
//
// Listeners
onPathInputChange = ({ value }) => {
this.setState({ currentPath: value });
}
};
onRowPress = (path) => {
this.props.onFetchPaths(path);
}
};
onOkPress = () => {
this.props.onChange({
@@ -91,7 +91,7 @@ class FileBrowserModalContent extends Component {
this.props.onClearPaths();
this.props.onModalClose();
}
};
//
// Render

View File

@@ -78,16 +78,16 @@ class FileBrowserModalContentConnector extends Component {
allowFoldersWithoutTrailingSlashes: true,
includeFiles
});
}
};
onClearPaths = () => {
// this.props.dispatchClearPaths();
}
};
onModalClose = () => {
this.props.dispatchClearPaths();
this.props.onModalClose();
}
};
//
// Render

View File

@@ -28,7 +28,7 @@ class FileBrowserRow extends Component {
onPress = () => {
this.props.onPress(this.props.path);
}
};
//
// Render

View File

@@ -102,7 +102,7 @@ class DateFilterBuilderRowValue extends Component {
name: NAME,
value: newValue
});
}
};
onTimeChange = ({ value }) => {
const {
@@ -117,7 +117,7 @@ class DateFilterBuilderRowValue extends Component {
value: filterValue.value
}
});
}
};
//
// Render

View File

@@ -63,7 +63,7 @@ class FilterBuilderModalContent extends Component {
onLabelChange = ({ value }) => {
this.setState({ label: value });
}
};
onFilterChange = (index, filter) => {
const filters = [...this.state.filters];
@@ -72,7 +72,7 @@ class FilterBuilderModalContent extends Component {
this.setState({
filters
});
}
};
onAddFilterPress = () => {
const filters = [...this.state.filters];
@@ -81,7 +81,7 @@ class FilterBuilderModalContent extends Component {
this.setState({
filters
});
}
};
onRemoveFilterPress = (index) => {
const filters = [...this.state.filters];
@@ -90,7 +90,7 @@ class FilterBuilderModalContent extends Component {
this.setState({
filters
});
}
};
onSaveFilterPress = () => {
const {
@@ -122,7 +122,7 @@ class FilterBuilderModalContent extends Component {
label,
filters
});
}
};
//
// Render
@@ -166,7 +166,9 @@ class FilterBuilderModalContent extends Component {
</div>
</div>
<div className={styles.label}>Filters</div>
<div className={styles.label}>
{translate('Filters')}
</div>
<div className={styles.rows}>
{

View File

@@ -138,7 +138,7 @@ class FilterBuilderRow extends Component {
this.selectedFilterBuilderProp = selectedFilterBuilderProp;
onFilterChange(index, filter);
}
};
onFilterChange = ({ name, value }) => {
const {
@@ -158,7 +158,7 @@ class FilterBuilderRow extends Component {
filter[name] = value;
onFilterChange(index, filter);
}
};
onAddPress = () => {
const {
@@ -167,7 +167,7 @@ class FilterBuilderRow extends Component {
} = this.props;
onAddPress(index);
}
};
onRemovePress = () => {
const {
@@ -176,7 +176,7 @@ class FilterBuilderRow extends Component {
} = this.props;
onRemovePress(index);
}
};
//
// Render

View File

@@ -84,7 +84,7 @@ class FilterBuilderRowValue extends Component {
name: NAME,
value: [...filterValue, value]
});
}
};
onTagDelete = ({ index }) => {
const {
@@ -98,7 +98,7 @@ class FilterBuilderRowValue extends Component {
name: NAME,
value
});
}
};
//
// Render

View File

@@ -47,7 +47,7 @@ class IndexerFilterBuilderRowValueConnector extends Component {
if (!this.props.isPopulated) {
this.props.dispatchFetchIndexers();
}
}
};
//
// Render

View File

@@ -55,7 +55,7 @@ class CustomFilter extends Component {
} = this.props;
onEditPress(id);
}
};
onRemovePress = () => {
const {
@@ -67,7 +67,7 @@ class CustomFilter extends Component {
dispatchDeleteCustomFilter({ id });
});
}
};
//
// Render

View File

@@ -25,14 +25,14 @@ class FilterModal extends Component {
this.setState({
filterBuilder: true
});
}
};
onEditCustomFilter = (id) => {
this.setState({
filterBuilder: true,
id
});
}
};
onCancelPress = () => {
if (this.state.filterBuilder) {
@@ -43,7 +43,7 @@ class FilterModal extends Component {
} else {
this.onModalClose();
}
}
};
onModalClose = () => {
this.setState({
@@ -52,7 +52,7 @@ class FilterModal extends Component {
}, () => {
this.props.onModalClose();
});
}
};
//
// Render

View File

@@ -69,7 +69,7 @@ class AppProfileSelectInputConnector extends Component {
onChange = ({ name, value }) => {
this.props.onChange({ name, value: parseInt(value) });
}
};
//
// Render

View File

@@ -35,11 +35,11 @@ class AutoCompleteInput extends Component {
name: this.props.name,
value: newValue
});
}
};
onInputBlur = () => {
this.setState({ suggestions: [] });
}
};
onSuggestionsFetchRequested = ({ value }) => {
const { values } = this.props;
@@ -50,11 +50,11 @@ class AutoCompleteInput extends Component {
});
this.setState({ suggestions: filteredValues });
}
};
onSuggestionsClearRequested = () => {
this.setState({ suggestions: [] });
}
};
//
// Render

View File

@@ -49,7 +49,7 @@ class AutoSuggestInput extends Component {
}}
</Reference>
);
}
};
renderSuggestionsContainer = ({ containerProps, children }) => {
return (
@@ -90,7 +90,7 @@ class AutoSuggestInput extends Component {
</Popper>
</Portal>
);
}
};
//
// Listeners
@@ -113,14 +113,14 @@ class AutoSuggestInput extends Component {
data.styles.width = width;
return data;
}
};
onInputChange = (event, { newValue }) => {
this.props.onChange({
name: this.props.name,
value: newValue
});
}
};
onInputKeyDown = (event) => {
const {
@@ -144,7 +144,7 @@ class AutoSuggestInput extends Component {
});
}
}
}
};
//
// Render

View File

@@ -39,7 +39,7 @@ class CaptchaInputConnector extends Component {
componentWillUnmount = () => {
this.props.resetCaptcha();
}
};
//
// Listeners
@@ -51,7 +51,7 @@ class CaptchaInputConnector extends Component {
} = this.props;
this.props.refreshCaptcha({ provider, providerData });
}
};
onCaptchaChange = (captchaResponse) => {
// If the captcha has expired `captchaResponse` will be null.
@@ -68,7 +68,7 @@ class CaptchaInputConnector extends Component {
} = this.props;
this.props.getCaptchaCookie({ provider, providerData, captchaResponse });
}
};
//
// Render

View File

@@ -31,7 +31,7 @@ class CardigannCaptchaInputConnector extends Component {
componentWillUnmount = () => {
this.props.resetCaptcha();
}
};
//
// Listeners
@@ -48,7 +48,7 @@ class CardigannCaptchaInputConnector extends Component {
this.props.resetCaptcha();
this.props.refreshCaptcha({ provider, providerData });
}
};
//
// Render

View File

@@ -59,14 +59,14 @@ class CheckInput extends Component {
shiftKey
});
}
}
};
//
// Listeners
setRef = (ref) => {
this._checkbox = ref;
}
};
onClick = (event) => {
if (this.props.isDisabled) {
@@ -78,14 +78,14 @@ class CheckInput extends Component {
event.preventDefault();
this.toggleChecked(checked, shiftKey);
}
};
onChange = (event) => {
const checked = event.target.checked;
const shiftKey = event.nativeEvent.shiftKey;
this.toggleChecked(checked, shiftKey);
}
};
//
// Render

View File

@@ -23,7 +23,7 @@ class DeviceInput extends Component {
name,
value: [...value, deviceId]
});
}
};
onTagDelete = ({ index }) => {
const {
@@ -39,7 +39,7 @@ class DeviceInput extends Component {
name,
value: newValue
});
}
};
//
// Render

View File

@@ -48,11 +48,11 @@ class DeviceInputConnector extends Component {
componentDidMount = () => {
this._populate();
}
};
componentWillUnmount = () => {
this.props.dispatchClearOptions({ section: 'devices' });
}
};
//
// Control
@@ -77,7 +77,7 @@ class DeviceInputConnector extends Component {
onRefreshPress = () => {
this._populate();
}
};
//
// Render

View File

@@ -149,7 +149,7 @@ class EnhancedSelectInput extends Component {
}
return data;
}
};
onWindowClick = (event) => {
const button = document.getElementById(this._buttonId);
@@ -168,14 +168,14 @@ class EnhancedSelectInput extends Component {
this.setState({ isOpen: false });
this._removeListener();
}
}
};
onFocus = () => {
if (this.state.isOpen) {
this._removeListener();
this.setState({ isOpen: false });
}
}
};
onBlur = () => {
if (!this.props.isEditable) {
@@ -186,7 +186,7 @@ class EnhancedSelectInput extends Component {
this.setState({ selectedIndex: origIndex });
}
}
}
};
onKeyDown = (event) => {
const {
@@ -253,7 +253,7 @@ class EnhancedSelectInput extends Component {
if (!_.isEmpty(newState)) {
this.setState(newState);
}
}
};
onPress = () => {
if (this.state.isOpen) {
@@ -267,7 +267,7 @@ class EnhancedSelectInput extends Component {
}
this.setState({ isOpen: !this.state.isOpen });
}
};
onSelect = (value) => {
if (Array.isArray(this.props.value)) {
@@ -291,15 +291,15 @@ class EnhancedSelectInput extends Component {
value
});
}
}
};
onMeasure = ({ width }) => {
this.setState({ width });
}
};
onOptionsModalClose = () => {
this.setState({ isOpen: false });
}
};
//
// Render

View File

@@ -73,7 +73,7 @@ class EnhancedSelectInputConnector extends Component {
componentDidMount = () => {
this._populate();
}
};
componentDidUpdate = (prevProps) => {
const prevKey = getProviderDataKey(prevProps.providerData);
@@ -82,11 +82,11 @@ class EnhancedSelectInputConnector extends Component {
if (!_.isEqual(prevKey, nextKey)) {
this.setState({ refetchRequired: true });
}
}
};
componentWillUnmount = () => {
this._cleanup();
}
};
//
// Listeners
@@ -95,7 +95,7 @@ class EnhancedSelectInputConnector extends Component {
if (this.state.refetchRequired) {
this._populate();
}
}
};
//
// Control

View File

@@ -21,11 +21,11 @@ class EnhancedSelectInputOption extends Component {
} = this.props;
onSelect(id);
}
};
onCheckPress = () => {
// CheckInput requires a handler. Swallow the change event because onPress will already handle it via event propagation.
}
};
//
// Render

View File

@@ -43,7 +43,7 @@ class IndexerFlagsSelectInputConnector extends Component {
});
this.props.onChange({ name, value: indexerFlags });
}
};
//
// Render

View File

@@ -43,7 +43,7 @@ class IndexersSelectInputConnector extends Component {
onChange = ({ name, value }) => {
this.props.onChange({ name, value });
}
};
//
// Render

View File

@@ -39,7 +39,7 @@ class KeyValueListInput extends Component {
name,
value: newValue
});
}
};
onRemoveItem = (index) => {
const {
@@ -55,13 +55,13 @@ class KeyValueListInput extends Component {
name,
value: newValue
});
}
};
onFocus = () => {
this.setState({
isFocused: true
});
}
};
onBlur = () => {
this.setState({
@@ -88,7 +88,7 @@ class KeyValueListInput extends Component {
value: newValue
});
}
}
};
//
// Render

View File

@@ -18,7 +18,7 @@ class KeyValueListInputItem extends Component {
} = this.props;
onChange(index, { key: keyValue, value });
}
};
onValueChange = ({ value }) => {
// TODO: Validate here or validate at a lower level component
@@ -30,7 +30,7 @@ class KeyValueListInputItem extends Component {
} = this.props;
onChange(index, { key: keyValue, value });
}
};
onRemovePress = () => {
const {
@@ -39,15 +39,15 @@ class KeyValueListInputItem extends Component {
} = this.props;
onRemove(index);
}
};
onFocus = () => {
this.props.onFocus();
}
};
onBlur = () => {
this.props.onBlur();
}
};
//
// Render

View File

@@ -42,7 +42,7 @@ class IndexersSelectInputConnector extends Component {
onChange = ({ name, value }) => {
this.props.onChange({ name, value });
}
};
//
// Render

View File

@@ -59,11 +59,11 @@ class NumberInput extends Component {
value: parseValue(this.props, value)
});
}
};
onFocus = () => {
this.setState({ isFocused: true });
}
};
onBlur = () => {
const {
@@ -88,7 +88,7 @@ class NumberInput extends Component {
name,
value: parsedValue
});
}
};
//
// Render

View File

@@ -41,7 +41,7 @@ class OAuthInputConnector extends Component {
componentWillUnmount = () => {
this.props.resetOAuth();
}
};
//
// Listeners
@@ -60,7 +60,7 @@ class OAuthInputConnector extends Component {
providerData,
section
});
}
};
//
// Render

View File

@@ -62,7 +62,7 @@ class PathInput extends Component {
onInputChange = ({ value }) => {
this.setState({ value });
}
};
onInputKeyDown = (event) => {
if (event.key === 'Tab') {
@@ -80,7 +80,7 @@ class PathInput extends Component {
}
}
}
}
};
onInputBlur = () => {
this.props.onChange({
@@ -89,28 +89,28 @@ class PathInput extends Component {
});
this.props.onClearPaths();
}
};
onSuggestionsFetchRequested = ({ value }) => {
this.props.onFetchPaths(value);
}
};
onSuggestionsClearRequested = () => {
// Required because props aren't always rendered, but no-op
// because we don't want to reset the paths after a path is selected.
}
};
onSuggestionSelected = (event, { suggestionValue }) => {
this.props.onFetchPaths(suggestionValue);
}
};
onFileBrowserOpenPress = () => {
this.setState({ isFileBrowserModalOpen: true });
}
};
onFileBrowserModalClose = () => {
this.setState({ isFileBrowserModalOpen: false });
}
};
//
// Render

View File

@@ -47,11 +47,11 @@ class PathInputConnector extends Component {
path,
includeFiles
});
}
};
onClearPaths = () => {
this.props.dispatchClearPaths();
}
};
//
// Render

View File

@@ -68,6 +68,7 @@ function ProviderFieldFormGroup(props) {
label,
helpText,
helpLink,
placeholder,
value,
type,
advanced,
@@ -100,6 +101,7 @@ function ProviderFieldFormGroup(props) {
label={label}
helpText={helpText}
helpLink={helpLink}
placeholder={placeholder}
value={value}
values={getSelectValues(selectOptions)}
errors={errors}
@@ -125,6 +127,7 @@ ProviderFieldFormGroup.propTypes = {
label: PropTypes.string,
helpText: PropTypes.string,
helpLink: PropTypes.string,
placeholder: PropTypes.string,
value: PropTypes.any,
type: PropTypes.string.isRequired,
advanced: PropTypes.bool.isRequired,

View File

@@ -13,7 +13,7 @@ class SelectInput extends Component {
name: this.props.name,
value: event.target.value
});
}
};
//
// Render

View File

@@ -49,7 +49,7 @@ class TagInput extends Component {
_setAutosuggestRef = (ref) => {
this._autosuggestRef = ref;
}
};
getSuggestionValue({ name }) {
return name;
@@ -57,7 +57,7 @@ class TagInput extends Component {
shouldRenderSuggestions = (value) => {
return value.length >= this.props.minQueryLength;
}
};
renderSuggestion({ name }) {
return name;
@@ -70,14 +70,14 @@ class TagInput extends Component {
value: '',
suggestions: []
});
}, 250, { leading: true, trailing: false })
}, 250, { leading: true, trailing: false });
//
// Listeners
onInputContainerPress = () => {
this._autosuggestRef.input.focus();
}
};
onInputChange = (event, { newValue, method }) => {
const value = _.isObject(newValue) ? newValue.name : newValue;
@@ -85,7 +85,7 @@ class TagInput extends Component {
if (method === 'type') {
this.setState({ value });
}
}
};
onInputKeyDown = (event) => {
const {
@@ -125,11 +125,11 @@ class TagInput extends Component {
event.preventDefault();
}
}
}
};
onInputFocus = () => {
this.setState({ isFocused: true });
}
};
onInputBlur = () => {
this.setState({ isFocused: false });
@@ -153,7 +153,7 @@ class TagInput extends Component {
if (tag) {
this.addTag(tag);
}
}
};
onSuggestionsFetchRequested = ({ value }) => {
const lowerCaseValue = value.toLowerCase();
@@ -170,16 +170,16 @@ class TagInput extends Component {
});
this.setState({ suggestions });
}
};
onSuggestionsClearRequested = () => {
// Required because props aren't always rendered, but no-op
// because we don't want to reset the paths after a path is selected.
}
};
onSuggestionSelected = (event, { suggestion }) => {
this.addTag(suggestion);
}
};
//
// Render
@@ -204,7 +204,7 @@ class TagInput extends Component {
onInputContainerPress={this.onInputContainerPress}
/>
);
}
};
render() {
const {

View File

@@ -101,7 +101,7 @@ class TagInputConnector extends Component {
newValue.push(tag.id);
this.props.onChange({ name, value: newValue });
}
};
onTagDelete = ({ index }) => {
const {
@@ -116,7 +116,7 @@ class TagInputConnector extends Component {
name,
value: newValue
});
}
};
onTagCreated = (tag) => {
const {
@@ -128,7 +128,7 @@ class TagInputConnector extends Component {
newValue.push(tag.id);
this.props.onChange({ name, value: newValue });
}
};
//
// Render

View File

@@ -19,7 +19,7 @@ class TagInputInput extends Component {
}
onInputContainerPress();
}
};
render() {
const {

View File

@@ -22,7 +22,7 @@ class TagInputTag extends Component {
index,
id: tag.id
});
}
};
//
// Render

View File

@@ -60,7 +60,7 @@ class TagSelectInputConnector extends Component {
}
this.props.onChange({ name, value: newValue });
}
};
onTagDelete = ({ index }) => {
const {
@@ -75,7 +75,7 @@ class TagSelectInputConnector extends Component {
name,
value: newValue
});
}
};
//
// Render

View File

@@ -35,7 +35,7 @@ class TextArea extends Component {
setInputRef = (ref) => {
this._input = ref;
}
};
selectionChange() {
if (this._selectionTimeout) {
@@ -75,7 +75,7 @@ class TextArea extends Component {
};
onChange(payload);
}
};
onFocus = (event) => {
if (this.props.onFocus) {
@@ -83,19 +83,19 @@ class TextArea extends Component {
}
this.selectionChange();
}
};
onKeyUp = () => {
this.selectionChange();
}
};
onMouseDown = () => {
this._isMouseTarget = true;
}
};
onMouseUp = () => {
this.selectionChange();
}
};
onDocumentMouseUp = () => {
if (this._isMouseTarget) {
@@ -103,7 +103,7 @@ class TextArea extends Component {
}
this._isMouseTarget = false;
}
};
//
// Render

View File

@@ -35,7 +35,7 @@ class TextInput extends Component {
setInputRef = (ref) => {
this._input = ref;
}
};
selectionChange() {
if (this._selectionTimeout) {
@@ -82,7 +82,7 @@ class TextInput extends Component {
}
onChange(payload);
}
};
onFocus = (event) => {
if (this.props.onFocus) {
@@ -90,19 +90,19 @@ class TextInput extends Component {
}
this.selectionChange();
}
};
onKeyUp = () => {
this.selectionChange();
}
};
onMouseDown = () => {
this._isMouseTarget = true;
}
};
onMouseUp = () => {
this.selectionChange();
}
};
onDocumentMouseUp = () => {
if (this._isMouseTarget) {
@@ -110,7 +110,7 @@ class TextInput extends Component {
}
this._isMouseTarget = false;
}
};
//
// Render

View File

@@ -53,7 +53,7 @@ class TextTagInputConnector extends Component {
});
onChange({ name, value: newValue.join(',') });
}
};
onTagDelete = ({ index }) => {
const {
@@ -69,7 +69,7 @@ class TextTagInputConnector extends Component {
name,
value: newValue.join(',')
});
}
};
//
// Render

View File

@@ -63,7 +63,7 @@ class ClipboardButton extends Component {
showSuccess: false,
showError: false
});
}
};
//
// Listeners
@@ -72,13 +72,13 @@ class ClipboardButton extends Component {
this.setState({
showSuccess: true
});
}
};
onError = () => {
this.setState({
showError: true
});
}
};
//
// Render

View File

@@ -18,7 +18,7 @@ class Link extends Component {
if (!isDisabled && onPress) {
onPress(event);
}
}
};
//
// Render

View File

@@ -90,7 +90,7 @@ class SpinnerErrorButton extends Component {
hasWarning: false,
hasError: false
});
}
};
//
// Render

View File

@@ -17,7 +17,7 @@ class Measure extends Component {
onMeasure = _.debounce((payload) => {
this.props.onMeasure(payload);
}, 250, { leading: true, trailing: false })
}, 250, { leading: true, trailing: false });
//
// Render

View File

@@ -25,11 +25,11 @@ class FilterMenu extends Component {
onCustomFiltersPress = () => {
this.setState({ isFilterModalOpen: true });
}
};
onFiltersModalClose = () => {
this.setState({ isFilterModalOpen: false });
}
};
//
// Render

View File

@@ -14,7 +14,7 @@ class FilterMenuItem extends Component {
} = this.props;
onPress(filterKey);
}
};
//
// Render

View File

@@ -124,7 +124,7 @@ class Menu extends Component {
this.setState({ isMenuOpen: false });
this._removeListener();
}
}
};
onTouchStart = (event) => {
const menuButton = document.getElementById(this._menuButtonId);
@@ -148,17 +148,17 @@ class Menu extends Component {
this.setState({ isMenuOpen: false });
this._removeListener();
}
}
};
onWindowResize = () => {
this.setMaxHeight();
}
};
onWindowScroll = (event) => {
if (this.state.isMenuOpen) {
this.setMaxHeight();
}
}
};
onMenuButtonPress = () => {
const state = {
@@ -173,7 +173,7 @@ class Menu extends Component {
}
this.setState(state);
}
};
//
// Render

View File

@@ -14,7 +14,7 @@ class SearchMenuItem extends Component {
} = this.props;
onPress(name);
}
};
//
// Render

View File

@@ -17,7 +17,7 @@ class SelectedMenuItem extends Component {
} = this.props;
onPress(name);
}
};
//
// Render

View File

@@ -66,7 +66,7 @@ class Modal extends Component {
_setBackgroundRef = (ref) => {
this._backgroundRef = ref;
}
};
_openModal() {
openModals.push(this._modalId);
@@ -131,7 +131,7 @@ class Modal extends Component {
onBackdropBeginPress = (event) => {
this._isBackdropPressed = this._isBackdropTarget(event);
}
};
onBackdropEndPress = (event) => {
const {
@@ -148,7 +148,7 @@ class Modal extends Component {
}
this._isBackdropPressed = false;
}
};
onKeyDown = (event) => {
const keyCode = event.keyCode;
@@ -161,7 +161,7 @@ class Modal extends Component {
this.props.onModalClose();
}
}
}
};
//
// Render

View File

@@ -36,12 +36,12 @@ class IndexerSearchInput extends Component {
setAutosuggestRef = (ref) => {
this._autosuggest = ref;
}
};
focusInput = (event) => {
event.preventDefault();
this._autosuggest.input.focus();
}
};
getSectionSuggestions(section) {
return section.suggestions;
@@ -102,7 +102,7 @@ class IndexerSearchInput extends Component {
}
this.setState({ value: newValue });
}
};
onKeyDown = (event) => {
if (event.shiftKey || event.altKey || event.ctrlKey) {
@@ -137,31 +137,31 @@ class IndexerSearchInput extends Component {
this._autosuggest.input.blur();
this.reset();
}
};
onBlur = () => {
this.reset();
}
};
onSuggestionsClearRequested = () => {
this.setState({
suggestions: [],
loading: false
});
}
};
onSuggestionsFetchRequested = () => {
this.setState({
suggestions: [],
loading: false
});
}
};
onSuggestionSelected = (event, { suggestion }) => {
if (suggestion.type === ADD_NEW_TYPE) {
this.props.onGoToAddNewMovie(this.state.value);
}
}
};
//
// Render

View File

@@ -32,14 +32,14 @@ class PageHeader extends Component {
onOpenKeyboardShortcutsModal = () => {
this.setState({ isKeyboardShortcutsModalOpen: true });
}
};
//
// Listeners
onKeyboardShortcutsModalClose = () => {
this.setState({ isKeyboardShortcutsModalOpen: false });
}
};
//
// Render

View File

@@ -28,11 +28,11 @@ class PageHeaderActionsMenuConnector extends Component {
onRestartPress = () => {
this.props.restart();
}
};
onShutdownPress = () => {
this.props.shutdown();
}
};
//
// Render

View File

@@ -54,15 +54,15 @@ class Page extends Component {
width: window.innerWidth,
height: window.innerHeight
});
}
};
onUpdatedModalClose = () => {
this.setState({ isUpdatedModalOpen: false });
}
};
onConnectionLostModalClose = () => {
this.setState({ isConnectionLostModalOpen: false });
}
};
//
// Render

View File

@@ -232,7 +232,7 @@ class PageConnector extends Component {
onSidebarToggle = () => {
this.props.onSidebarVisibleChange(!this.props.isSidebarVisible);
}
};
//
// Render

View File

@@ -27,7 +27,7 @@ class PageContentBody extends Component {
if (this.props.onScroll && !isLocked()) {
onScroll(props);
}
}
};
//
// Render

View File

@@ -101,7 +101,7 @@ class PageJumpBar extends Component {
onMeasure = ({ height }) => {
this.setState({ height });
}
};
//
// Render

View File

@@ -15,7 +15,7 @@ class PageJumpBarItem extends Component {
} = this.props;
onItemPress(label);
}
};
//
// Render

View File

@@ -13,20 +13,8 @@ function getIconName(name) {
return icons.BACKUP;
case 'CheckHealth':
return icons.HEALTH;
case 'EpisodeSearch':
return icons.SEARCH;
case 'Housekeeping':
return icons.HOUSEKEEPING;
case 'RefreshMovie':
return icons.REFRESH;
case 'RssSync':
return icons.RSS;
case 'SeasonSearch':
return icons.SEARCH;
case 'MovieSearch':
return icons.SEARCH;
case 'UpdateSceneMapping':
return icons.REFRESH;
default:
return icons.SPINNER;
}

View File

@@ -35,11 +35,11 @@ class MessageConnector extends Component {
if (hideAfter) {
this._hideTimeoutId = setTimeout(this.hideMessage, hideAfter * 1000);
}
}
};
hideMessage = () => {
this.props.hideMessage({ id: this.props.id });
}
};
//
// Render

View File

@@ -234,7 +234,7 @@ class PageSidebar extends Component {
_setSidebarRef = (ref) => {
this._sidebarRef = ref;
}
};
_setSidebarTransform(isSidebarVisible, transition, callback) {
this.setState({
@@ -263,11 +263,11 @@ class PageSidebar extends Component {
event.stopPropagation();
this.props.onSidebarVisibleChange(false);
}
}
};
onWindowScroll = () => {
this.setState(getPositioning());
}
};
onTouchStart = (event) => {
const touches = event.touches;
@@ -287,7 +287,7 @@ class PageSidebar extends Component {
this._touchStartX = touchStartX;
this._touchStartY = touchStartY;
}
};
onTouchMove = (event) => {
const touches = event.touches;
@@ -324,7 +324,7 @@ class PageSidebar extends Component {
transition: 'none',
transform
});
}
};
onTouchEnd = (event) => {
const touches = event.changedTouches;
@@ -344,16 +344,16 @@ class PageSidebar extends Component {
this._touchStartX = null;
this._touchStartY = null;
}
};
onTouchCancel = (event) => {
this._touchStartX = null;
this._touchStartY = null;
}
};
onItemPress = () => {
this.props.onSidebarVisibleChange(false);
}
};
//
// Render

View File

@@ -21,7 +21,7 @@ class PageSidebarItem extends Component {
if (isChildItem || !isParentItem) {
onPress();
}
}
};
//
// Render

View File

@@ -108,7 +108,7 @@ class PageToolbarSection extends Component {
isMeasured: true,
width
});
}
};
//
// Render

View File

@@ -41,7 +41,7 @@ class OverlayScroller extends Component {
if (ref) {
this.props.registerScroller(ref.view);
}
}
};
_renderThumb = (props) => {
return (
@@ -50,7 +50,7 @@ class OverlayScroller extends Component {
{...props}
/>
);
}
};
_renderTrackHorizontal = ({ style, props }) => {
const finalStyle = {
@@ -69,7 +69,7 @@ class OverlayScroller extends Component {
{...props}
/>
);
}
};
_renderTrackVertical = ({ style, props }) => {
const finalStyle = {
@@ -88,7 +88,7 @@ class OverlayScroller extends Component {
{...props}
/>
);
}
};
_renderView = (props) => {
return (
@@ -97,18 +97,18 @@ class OverlayScroller extends Component {
{...props}
/>
);
}
};
//
// Listers
onScrollStart = () => {
this._isScrolling = true;
}
};
onScrollStop = () => {
this._isScrolling = false;
}
};
onScroll = (event) => {
const {
@@ -122,7 +122,7 @@ class OverlayScroller extends Component {
if (onScroll) {
onScroll({ scrollTop, scrollLeft });
}
}
};
//
// Render

View File

@@ -38,7 +38,7 @@ class Scroller extends Component {
this._scroller = ref;
this.props.registerScroller(ref);
}
};
//
// Render

View File

@@ -139,7 +139,7 @@ class SignalRConnector extends Component {
}
console.error(`signalR: Unable to find handler for ${name}`);
}
};
handleCommand = (body) => {
if (body.action === 'sync') {
@@ -158,15 +158,15 @@ class SignalRConnector extends Component {
} else {
this.props.dispatchUpdateCommand(resource);
}
}
};
handleHealth = () => {
this.props.dispatchFetchHealth();
}
};
handleIndexerstatus = () => {
this.props.dispatchFetchIndexerStatus();
}
};
handleIndexer = (body) => {
const action = body.action;
@@ -177,17 +177,17 @@ class SignalRConnector extends Component {
} else if (action === 'deleted') {
this.props.dispatchRemoveItem({ section, id: body.resource.id });
}
}
};
handleVersion = (body) => {
const version = body.version;
this.props.dispatchSetVersion({ version });
}
};
handleSystemTask = () => {
this.props.dispatchFetchCommands();
}
};
handleTag = (body) => {
if (body.action === 'sync') {
@@ -195,7 +195,7 @@ class SignalRConnector extends Component {
this.props.dispatchFetchTagDetails();
return;
}
}
};
//
// Listeners
@@ -210,7 +210,7 @@ class SignalRConnector extends Component {
isDisconnected: false,
isRestarting: false
});
}
};
onStart = () => {
console.debug('[signalR] connected');
@@ -221,11 +221,11 @@ class SignalRConnector extends Component {
isDisconnected: false,
isRestarting: false
});
}
};
onReconnecting = () => {
this.props.dispatchSetAppValue({ isReconnecting: true });
}
};
onReconnected = () => {
@@ -247,17 +247,17 @@ class SignalRConnector extends Component {
dispatchFetchIndexers();
dispatchFetchCommands();
repopulatePage();
}
};
onClose = () => {
console.debug('[signalR] connection closed');
}
};
onReceiveMessage = (message) => {
console.debug('[signalR] received', message.name, message.body);
this.handleMessage(message);
}
};
//
// Render

View File

@@ -38,7 +38,7 @@ class TableSelectCell extends Component {
} = this.props;
onSelectedChange({ id, value, shiftKey });
}
};
//
// Render

View File

@@ -35,7 +35,7 @@ class VirtualTableSelectCell extends Component {
} = this.props;
onSelectedChange({ id, value, shiftKey });
}
};
//
// Render

View File

@@ -21,7 +21,7 @@ class TableHeaderCell extends Component {
} else {
this.props.onSortPress(name);
}
}
};
//
// Render

View File

@@ -62,7 +62,7 @@ class TableOptionsModal extends Component {
pageSize: value,
pageSizeError
});
}
};
onVisibleChange = ({ name, value }) => {
const columns = _.cloneDeep(this.props.columns);
@@ -71,7 +71,7 @@ class TableOptionsModal extends Component {
column.isVisible = value;
this.props.onTableOptionChange({ columns });
}
};
onColumnDragMove = (dragIndex, dropIndex) => {
if (this.state.dragIndex !== dragIndex || this.state.dropIndex !== dropIndex) {
@@ -80,7 +80,7 @@ class TableOptionsModal extends Component {
dropIndex
});
}
}
};
onColumnDragEnd = ({ id }, didDrop) => {
const {
@@ -100,7 +100,7 @@ class TableOptionsModal extends Component {
dragIndex: null,
dropIndex: null
});
}
};
//
// Render

View File

@@ -20,11 +20,11 @@ class TableOptionsModalWrapper extends Component {
onTableOptionsPress = () => {
this.setState({ isTableOptionsModalOpen: true });
}
};
onTableOptionsModalClose = () => {
this.setState({ isTableOptionsModalOpen: false });
}
};
//
// Render

View File

@@ -26,16 +26,16 @@ class TablePager extends Component {
onOpenPageSelectClick = () => {
this.setState({ isShowingPageSelect: true });
}
};
onPageSelect = ({ value: page }) => {
this.setState({ isShowingPageSelect: false });
this.props.onPageSelect(parseInt(page));
}
};
onPageSelectBlur = () => {
this.setState({ isShowingPageSelect: false });
}
};
//
// Render

View File

@@ -75,7 +75,7 @@ class VirtualTable extends Component {
setGridRef = (ref) => {
this._grid = ref;
}
};
//
// Listeners
@@ -84,7 +84,7 @@ class VirtualTable extends Component {
this.setState({
width
});
}
};
//
// Render

View File

@@ -38,7 +38,7 @@ class VirtualTableHeaderCell extends Component {
} else {
this.props.onSortPress(name);
}
}
};
//
// Render

View File

@@ -80,20 +80,20 @@ class Tooltip extends Component {
}
return data;
}
};
//
// Listeners
onMeasure = ({ width }) => {
this.setState({ width });
}
};
onClick = () => {
if (isMobileUtil()) {
this.setState({ isOpen: !this.state.isOpen });
}
}
};
onMouseEnter = () => {
if (this._closeTimeout) {
@@ -101,13 +101,13 @@ class Tooltip extends Component {
}
this.setState({ isOpen: true });
}
};
onMouseLeave = () => {
this._closeTimeout = setTimeout(() => {
this.setState({ isOpen: false });
}, 100);
}
};
//
// Render

View File

@@ -64,12 +64,12 @@ function keyboardShortcuts(WrappedComponent) {
bindShortcut = (key, callback, options = {}) => {
this._mousetrap.bind(key, callback);
this._mousetrapBindings[key] = options;
}
};
unbindShortcut = (key) => {
delete this._mousetrapBindings[key];
this._mousetrap.unbind(key);
}
};
unbindAllShortcuts = () => {
const keys = Object.keys(this._mousetrapBindings);
@@ -83,7 +83,7 @@ function keyboardShortcuts(WrappedComponent) {
});
this._mousetrapBindings = {};
}
};
stopCallback = (event, element, combo) => {
const binding = this._mousetrapBindings[combo];
@@ -98,7 +98,7 @@ function keyboardShortcuts(WrappedComponent) {
element.tagName === 'TEXTAREA' ||
(element.contentEditable && element.contentEditable === 'true')
);
}
};
//
// Render

View File

@@ -22,10 +22,12 @@ import {
import {
faArrowCircleLeft as fasArrowCircleLeft,
faArrowCircleRight as fasArrowCircleRight,
faAsterisk as fasAsterisk,
faBackward as fasBackward,
faBan as fasBan,
faBars as fasBars,
faBolt as fasBolt,
faBook as fasBook,
faBookmark as fasBookmark,
faBookReader as fasBookReader,
faBroadcastTower as fasBroadcastTower,
@@ -74,6 +76,7 @@ import {
faLock as fasLock,
faMedkit as fasMedkit,
faMinus as fasMinus,
faMusic as fasMusic,
faPause as fasPause,
faPlay as fasPlay,
faPlus as fasPlus,
@@ -104,6 +107,7 @@ import {
faTimes as fasTimes,
faTimesCircle as fasTimesCircle,
faTrashAlt as fasTrashAlt,
faTv as fasTv,
faUser as fasUser,
faUserPlus as fasUserPlus,
faVial as fasVial,
@@ -121,7 +125,9 @@ export const ADVANCED_SETTINGS = fasCog;
export const ANNOUNCED = fasBullhorn;
export const ARROW_LEFT = fasArrowCircleLeft;
export const ARROW_RIGHT = fasArrowCircleRight;
export const AUDIO = fasMusic;
export const BACKUP = farFileArchive;
export const BOOK = fasBook;
export const BUG = fasBug;
export const CALENDAR = fasCalendarAlt;
export const CALENDAR_O = farCalendar;
@@ -158,6 +164,7 @@ export const FILTER = fasFilter;
export const FLAG = fasFlag;
export const FOLDER = farFolder;
export const FOLDER_OPEN = fasFolderOpen;
export const FOOTNOTE = fasAsterisk;
export const GENRE = fasTheaterMasks;
export const GROUP = farObjectGroup;
export const HEALTH = fasMedkit;
@@ -220,6 +227,7 @@ export const TAGS = fasTags;
export const TBA = fasQuestionCircle;
export const TEST = fasVial;
export const TRANSLATE = fasLanguage;
export const TV = fasTv;
export const UNGROUP = farObjectUngroup;
export const UNKNOWN = fasQuestion;
export const UNMONITORED = farBookmark;

View File

@@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import DescriptionList from 'Components/DescriptionList/DescriptionList';
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
import Link from 'Components/Link/Link';
import translate from 'Utilities/String/translate';
import styles from './HistoryDetails.css';
@@ -17,7 +18,8 @@ function HistoryDetails(props) {
query,
queryResults,
categories,
source
source,
url
} = data;
return (
@@ -39,7 +41,7 @@ function HistoryDetails(props) {
{
!!data &&
<DescriptionListItem
title={'Query Results'}
title={translate('QueryResults')}
data={queryResults ? queryResults : '-'}
/>
}
@@ -47,7 +49,7 @@ function HistoryDetails(props) {
{
!!data &&
<DescriptionListItem
title={'Categories'}
title={translate('Categories')}
data={categories ? categories : '-'}
/>
}
@@ -55,10 +57,18 @@ function HistoryDetails(props) {
{
!!data &&
<DescriptionListItem
title={'Source'}
title={translate('Source')}
data={source}
/>
}
{
!!data &&
<DescriptionListItem
title={translate('Url')}
data={url ? <Link to={url}>{translate('Link')}</Link> : '-'}
/>
}
</DescriptionList>
);
}
@@ -66,7 +76,8 @@ function HistoryDetails(props) {
if (eventType === 'releaseGrabbed') {
const {
source,
title
title,
url
} = data;
return (
@@ -82,7 +93,7 @@ function HistoryDetails(props) {
{
!!data &&
<DescriptionListItem
title={'Source'}
title={translate('Source')}
data={source ? source : '-'}
/>
}
@@ -90,10 +101,18 @@ function HistoryDetails(props) {
{
!!data &&
<DescriptionListItem
title={'Title'}
title={translate('Title')}
data={title ? title : '-'}
/>
}
{
!!data &&
<DescriptionListItem
title={translate('Url')}
data={url ? <Link to={url}>{translate('Link')}</Link> : '-'}
/>
}
</DescriptionList>
);
}

View File

@@ -35,16 +35,16 @@ class History extends Component {
onClearHistoryPress = () => {
this.setState({ isClearHistoryModalOpen: true });
}
};
onClearHistoryModalClose = () => {
this.setState({ isClearHistoryModalOpen: false });
}
};
onConfirmClearHistory = () => {
this.setState({ isClearHistoryModalOpen: false });
this.props.onClearHistoryPress();
}
};
//
// Render

View File

@@ -69,42 +69,42 @@ class HistoryConnector extends Component {
repopulate = () => {
this.props.fetchHistory();
}
};
//
// Listeners
onFirstPagePress = () => {
this.props.gotoHistoryFirstPage();
}
};
onPreviousPagePress = () => {
this.props.gotoHistoryPreviousPage();
}
};
onNextPagePress = () => {
this.props.gotoHistoryNextPage();
}
};
onLastPagePress = () => {
this.props.gotoHistoryLastPage();
}
};
onPageSelect = (page) => {
this.props.gotoHistoryPage({ page });
}
};
onSortPress = (sortKey) => {
this.props.setHistorySort({ sortKey });
}
};
onFilterSelect = (selectedFilterKey) => {
this.props.setHistoryFilter({ selectedFilterKey });
}
};
onClearHistoryPress = () => {
this.props.executeCommand({ name: commandNames.CLEAR_HISTORY });
}
};
onTableOptionChange = (payload) => {
this.props.setHistoryTableOption(payload);
@@ -112,7 +112,7 @@ class HistoryConnector extends Component {
if (payload.pageSize) {
this.props.gotoHistoryFirstPage();
}
}
};
//
// Render

View File

@@ -4,6 +4,7 @@ import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import { inputTypes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
class HistoryOptions extends Component {
@@ -43,7 +44,7 @@ class HistoryOptions extends Component {
this.setState(setting, () => {
dispatchSaveGeneralSettings(setting);
});
}
};
//
// Render
@@ -56,14 +57,14 @@ class HistoryOptions extends Component {
return (
<Fragment>
<FormGroup>
<FormLabel>History Cleanup</FormLabel>
<FormLabel>{translate('HistoryCleanup')}</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="historyCleanupDays"
value={historyCleanupDays}
helpText="Set to 0 to disable automatic cleanup"
helpTextWarning="History items older than the selected number of days will be cleaned up automatically"
helpText={translate('HistoryCleanupDaysHelpText')}
helpTextWarning={translate('HistoryCleanupDaysHelpTextWarning')}
onChange={this.onGlobalInputChange}
/>
</FormGroup>

View File

@@ -52,15 +52,15 @@ class HistoryRow extends Component {
}
this.props.onSearchPress(data.query, indexer.id, categories);
}
};
onDetailsPress = () => {
this.setState({ isDetailsModalOpen: true });
}
};
onDetailsModalClose = () => {
this.setState({ isDetailsModalOpen: false });
}
};
//
// Render
@@ -247,6 +247,21 @@ class HistoryRow extends Component {
);
}
if (name === 'grabTitle') {
return (
<TableRowCell
key={name}
className={styles.indexer}
>
{
data.title ?
data.title :
null
}
</TableRowCell>
);
}
if (name === 'categories') {
return (
<TableRowCell

View File

@@ -51,11 +51,11 @@ class HistoryRowConnector extends Component {
onSearchPress = (term, indexerId, categories) => {
this.props.setSearchDefault({ searchQuery: term, searchIndexerIds: [indexerId], searchCategories: categories });
this.props.push(`${window.Prowlarr.urlBase}/search`);
}
};
onMarkAsFailedPress = () => {
this.props.markAsFailed({ id: this.props.id });
}
};
//
// Render

View File

@@ -16,7 +16,7 @@
composes: input from '~Components/Form/TextInput.css';
flex: 0 0 auto;
margin-bottom: 20px;
margin-bottom: 16px;
}
.alert {
@@ -28,3 +28,46 @@
.scroller {
flex: 1 1 auto;
}
.filterRow {
display: flex;
margin-bottom: 20px;
}
.filterContainer {
display: flex;
align-items: stretch;
flex: 1;
flex-direction: column;
margin-right: 12px;
}
.filterContainer:last-child {
margin-right: 0;
}
.filterLabel {
margin-bottom: 3px;
font-weight: bold;
}
@media only screen and (max-width: $breakpointSmall) {
.alert {
display: none;
}
.filterRow {
flex-direction: column;
}
.filterContainer {
margin-right: 0;
margin-bottom: 12px;
}
.scroller {
margin-right: -30px;
margin-bottom: -30px;
margin-left: -30px;
}
}

View File

@@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Alert from 'Components/Alert';
import EnhancedSelectInput from 'Components/Form/EnhancedSelectInput';
import TextInput from 'Components/Form/TextInput';
import Button from 'Components/Link/Button';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
@@ -44,6 +45,32 @@ const columns = [
}
];
const protocols = [
{
key: 'torrent',
value: 'torrent'
},
{
key: 'usenet',
value: 'nzb'
}
];
const privacyLevels = [
{
key: 'private',
value: translate('Private')
},
{
key: 'semiPrivate',
value: translate('SemiPrivate')
},
{
key: 'public',
value: translate('Public')
}
];
class AddIndexerModalContent extends Component {
//
@@ -53,7 +80,10 @@ class AddIndexerModalContent extends Component {
super(props, context);
this.state = {
filter: ''
filter: '',
filterProtocols: [],
filterLanguages: [],
filterPrivacyLevels: []
};
}
@@ -62,7 +92,7 @@ class AddIndexerModalContent extends Component {
onFilterChange = ({ value }) => {
this.setState({ filter: value });
}
};
//
// Render
@@ -80,8 +110,31 @@ class AddIndexerModalContent extends Component {
onModalClose
} = this.props;
const filter = this.state.filter;
const filterLower = filter.toLowerCase();
const languages = Array.from(new Set(indexers.map(({ language }) => language)))
.sort((a, b) => a.localeCompare(b))
.map((language) => ({ key: language, value: language }));
const filteredIndexers = indexers.filter((indexer) => {
const { filter, filterProtocols, filterLanguages, filterPrivacyLevels } = this.state;
if (!indexer.name.toLowerCase().includes(filter.toLocaleLowerCase())) {
return false;
}
if (filterProtocols.length && !filterProtocols.includes(indexer.protocol)) {
return false;
}
if (filterLanguages.length && !filterLanguages.includes(indexer.language)) {
return false;
}
if (filterPrivacyLevels.length && !filterPrivacyLevels.includes(indexer.privacy)) {
return false;
}
return true;
});
const errorMessage = getErrorMessage(error, 'Unable to load indexers');
@@ -99,11 +152,43 @@ class AddIndexerModalContent extends Component {
className={styles.filterInput}
placeholder={translate('FilterPlaceHolder')}
name="filter"
value={filter}
value={this.state.filter}
autoFocus={true}
onChange={this.onFilterChange}
/>
<div className={styles.filterRow}>
<div className={styles.filterContainer}>
<label className={styles.filterLabel}>Protocol</label>
<EnhancedSelectInput
name="indexerProtocols"
value={this.state.filterProtocols}
values={protocols}
onChange={({ value }) => this.setState({ filterProtocols: value })}
/>
</div>
<div className={styles.filterContainer}>
<label className={styles.filterLabel}>Language</label>
<EnhancedSelectInput
name="indexerLanguages"
value={this.state.filterLanguages}
values={languages}
onChange={({ value }) => this.setState({ filterLanguages: value })}
/>
</div>
<div className={styles.filterContainer}>
<label className={styles.filterLabel}>Privacy</label>
<EnhancedSelectInput
name="indexerPrivacyLevels"
value={this.state.filterPrivacyLevels}
values={privacyLevels}
onChange={({ value }) => this.setState({ filterPrivacyLevels: value })}
/>
</div>
</div>
<Alert
kind={kinds.INFO}
className={styles.alert}
@@ -133,18 +218,14 @@ class AddIndexerModalContent extends Component {
>
<TableBody>
{
indexers.map((indexer) => {
return indexer.name.toLowerCase().includes(filterLower) ?
(
<SelectIndexerRow
key={indexer.name}
implementation={indexer.implementation}
{...indexer}
onIndexerSelect={onIndexerSelect}
/>
) :
null;
})
filteredIndexers.map((indexer) => (
<SelectIndexerRow
key={indexer.name}
implementation={indexer.implementation}
{...indexer}
onIndexerSelect={onIndexerSelect}
/>
))
}
</TableBody>
</Table> :

View File

@@ -52,11 +52,11 @@ class AddIndexerModalContentConnector extends Component {
onIndexerSelect = ({ implementation, name }) => {
this.props.selectIndexerSchema({ implementation, name });
this.props.onModalClose({ indexerSelected: true });
}
};
onSortPress = (sortKey, sortDirection) => {
this.props.setIndexerSchemaSort({ sortKey, sortDirection });
}
};
//
// Render

View File

@@ -17,7 +17,7 @@ class AddIndexerPresetMenuItem extends Component {
name,
implementation
});
}
};
//
// Render

View File

@@ -3,6 +3,8 @@ import React, { Component } from 'react';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRowButton from 'Components/Table/TableRowButton';
import ProtocolLabel from 'Indexer/Index/Table/ProtocolLabel';
import firstCharToUpper from 'Utilities/String/firstCharToUpper';
import translate from 'Utilities/String/translate';
import styles from './SelectIndexerRow.css';
class SelectIndexerRow extends Component {
@@ -17,7 +19,7 @@ class SelectIndexerRow extends Component {
} = this.props;
this.props.onIndexerSelect({ implementation, name });
}
};
//
// Render
@@ -47,7 +49,7 @@ class SelectIndexerRow extends Component {
</TableRowCell>
<TableRowCell>
{privacy}
{translate(firstCharToUpper(privacy))}
</TableRowCell>
</TableRowButton>
);

View File

@@ -27,11 +27,11 @@ class DeleteIndexerModalContent extends Component {
onDeleteFilesChange = ({ value }) => {
this.setState({ deleteFiles: value });
}
};
onAddImportExclusionChange = ({ value }) => {
this.setState({ addImportExclusion: value });
}
};
onDeleteMovieConfirmed = () => {
const deleteFiles = this.state.deleteFiles;
@@ -39,7 +39,7 @@ class DeleteIndexerModalContent extends Component {
this.setState({ deleteFiles: false, addImportExclusion: false });
this.props.onDeletePress(deleteFiles, addImportExclusion);
}
};
//
// Render

View File

@@ -32,7 +32,7 @@ class DeleteIndexerModalContentConnector extends Component {
});
this.props.onModalClose(true);
}
};
//
// Render

Some files were not shown because too many files have changed in this diff Show More