1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-03-06 13:31:28 -05:00

Compare commits

...

70 Commits

Author SHA1 Message Date
Qstick
df681d82be Fixed: Update Images on MovieMetadata refresh 2023-02-03 21:37:30 -06:00
Weblate
daf81c5b26 Translated using Weblate (French) [skip ci]
Currently translated at 97.5% (1129 of 1157 strings)

Translated using Weblate (Greek) [skip ci]

Currently translated at 100.0% (1157 of 1157 strings)

Translated using Weblate (Danish) [skip ci]

Currently translated at 89.9% (1041 of 1157 strings)

Translated using Weblate (Russian) [skip ci]

Currently translated at 100.0% (1157 of 1157 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1157 of 1157 strings)

Translated using Weblate (Greek) [skip ci]

Currently translated at 95.2% (1102 of 1157 strings)

Translated using Weblate (German) [skip ci]

Currently translated at 100.0% (1157 of 1157 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1157 of 1157 strings)

Translated using Weblate (Dutch) [skip ci]

Currently translated at 95.5% (1106 of 1157 strings)

Translated using Weblate (French) [skip ci]

Currently translated at 97.4% (1128 of 1157 strings)

Translated using Weblate (Slovak) [skip ci]

Currently translated at 21.4% (248 of 1157 strings)

Translated using Weblate (Chinese (Traditional) (zh_TW)) [skip ci]

Currently translated at 1.4% (17 of 1157 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN)) [skip ci]

Currently translated at 100.0% (1157 of 1157 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1157 of 1157 strings)

Translated using Weblate (Italian) [skip ci]

Currently translated at 94.4% (1093 of 1157 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN)) [skip ci]

Currently translated at 99.9% (1156 of 1157 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN)) [skip ci]

Currently translated at 99.9% (1156 of 1157 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1157 of 1157 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1157 of 1157 strings)

Translated using Weblate (Swedish) [skip ci]

Currently translated at 90.8% (1051 of 1157 strings)

Translated using Weblate (Hungarian) [skip ci]

Currently translated at 100.0% (1157 of 1157 strings)

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Csaba <csab0825@gmail.com>
Co-authored-by: Florian <sephrat.flo@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Uxose <nathan.renault@live.fr>
Co-authored-by: Vasilis Ieropoulos <kirav96@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: buzzke <buzzke@me.com>
Co-authored-by: hhjuhl <hans@kopula.dk>
Co-authored-by: oskhel <oskar.hellgren@gmail.com>
Co-authored-by: reloxx <reloxx@interia.pl>
Co-authored-by: ryabov-artem <art.rya@gmail.com>
Co-authored-by: zhuzhe1983 <zhuzhe1983@gmail.com>
Co-authored-by: 無情天 <kofzhanganguo@126.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/el/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/sk/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/sv/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/zh_CN/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/zh_TW/
Translation: Servarr/Radarr
2023-01-30 22:31:53 -06:00
Zak Saunders
78f929c60b Fixup File Name Tokens (#8036) 2023-01-30 22:30:37 -06:00
Qstick
87d59d12a4 Fixed: Avoid Sqlite Error when all profiles have lowest quality cutoff
(cherry picked from commit  f05e109b50cca496e7b42e2833eff161a43e12f4)
2023-01-26 16:36:25 +00:00
Mark McDowall
ce031124c7 Improve handling of releases without video files
New: Show warning in queue if download contains executable or archive file and no video file was detected

(cherry picked from commit b15b6a079846b21cac8476820fce9cde81732291)
2023-01-26 16:36:09 +00:00
Mark McDowall
d4ce08a044 Fixed: UTC time sent to UI for already imported message
(cherry picked from commit 3f598ffa6fbec90ecdbb266de4b0fe7558fbbc30)
2023-01-25 13:04:18 +00:00
Mark McDowall
871e78b314 Updated some JS dependencies 2023-01-25 13:03:33 +00:00
Mark McDowall
eeee682f6c New: Parse release group from VARYG releases with junk at the end
(cherry picked from commit 5ce8ea8985f880d4e68db852f04558a59461ae3d)
2023-01-25 13:02:25 +00:00
Qstick
9c594c3e53 Bump ImageSharp to 2.1.3
(cherry picked from commit c08b45156425d84e51072093d0ead42f1c105ad5)
2023-01-25 13:02:12 +00:00
voltron4lyfe
0b1b19a165 Fix Filter Button being squished #8024 2023-01-23 21:40:41 -06:00
Weblate
f1ff7b3b61 Translated using Weblate (Greek) [skip ci]
Currently translated at 95.2% (1102 of 1157 strings)

Translated using Weblate (German) [skip ci]

Currently translated at 100.0% (1157 of 1157 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1157 of 1157 strings)

Translated using Weblate (Dutch) [skip ci]

Currently translated at 95.5% (1106 of 1157 strings)

Translated using Weblate (French) [skip ci]

Currently translated at 97.4% (1128 of 1157 strings)

Translated using Weblate (Slovak) [skip ci]

Currently translated at 21.4% (248 of 1157 strings)

Translated using Weblate (Chinese (Traditional) (zh_TW)) [skip ci]

Currently translated at 1.4% (17 of 1157 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN)) [skip ci]

Currently translated at 100.0% (1157 of 1157 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1157 of 1157 strings)

Translated using Weblate (Italian) [skip ci]

Currently translated at 94.4% (1093 of 1157 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN)) [skip ci]

Currently translated at 99.9% (1156 of 1157 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN)) [skip ci]

Currently translated at 99.9% (1156 of 1157 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1157 of 1157 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1157 of 1157 strings)

Translated using Weblate (Swedish) [skip ci]

Currently translated at 90.8% (1051 of 1157 strings)

Translated using Weblate (Hungarian) [skip ci]

Currently translated at 100.0% (1157 of 1157 strings)

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Csaba <csab0825@gmail.com>
Co-authored-by: Florian <sephrat.flo@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Vasilis Ieropoulos <kirav96@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: buzzke <buzzke@me.com>
Co-authored-by: oskhel <oskar.hellgren@gmail.com>
Co-authored-by: reloxx <reloxx@interia.pl>
Co-authored-by: zhuzhe1983 <zhuzhe1983@gmail.com>
Co-authored-by: 無情天 <kofzhanganguo@126.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/el/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/sk/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/sv/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/zh_CN/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/zh_TW/
Translation: Servarr/Radarr
2023-01-23 19:21:10 -06:00
Robert Dailey
165c588557 Updated .gitignore to ignore .idea/ completely
Unless the repository owners wish to have these files in their repo,
they should be ignored so that contributors are not stepping around
these files.

[skip-ci]
2023-01-24 00:13:47 +00:00
Mark McDowall
327e18bc7a New: Filter by Custom Format Score in Interactive Search
Closes #7825

(cherry picked from commit 998768bcf2c3308611a05a4518e7ef3fbcb473cc)
2023-01-22 18:34:30 -06:00
Qstick
f61f2c89dc Remove OMG indexer
Closes #7527

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2023-01-22 18:27:24 -06:00
Qstick
2327b72558 New: Send additional information with Webhook and Custom Scripts
Closes #7730

Co-Authored-By: Devin Buhl <onedr0p@users.noreply.github.com>
2023-01-22 17:52:54 -06:00
Qstick
66ddd08684 New: Improve logging when processing files for import
Closes #7754
2023-01-22 17:41:24 -06:00
Robert Dailey
4d2143e9b2 Fixed: Unable to load UI if Quality Profiles contain removed Custom Format items
Manually cherry picked from Sonarr commits:

- 2c004e1f9665763111fcd964b81338bdbe735865
- 4b4301a076488c595969921697d7002ca427c955
2023-01-22 16:24:24 -06:00
Qstick
7906ea2a0c Rejection string improvements
Closes #7611
2023-01-22 14:27:15 -06:00
Zak Saunders
9d1956794e Fixed: Progress bar text colour in Dark theme
(cherry picked from commit ca61efa57fc04a7f6753aedb4b8044d17e345429)
2023-01-22 14:05:04 -06:00
voltron4lyfe
4956ff7914 Adding indicator display option to PageMenuButton. 2023-01-22 14:04:15 -06:00
Mark McDowall
f22a589cb8 Fixed: Filter indicator in interactive search
(cherry picked from commit 80d36a06c8f42f239304d2f5a7edcb573b5072db)
2023-01-22 14:04:15 -06:00
Qstick
04185d6839 Filter useless PG Errors from coming to Sentry 2023-01-22 11:46:52 -06:00
Qstick
fb25e5d577 Fixed: Catch InvalidDataException during initial config to prevent boot loop
[Common]
2023-01-21 13:06:18 -06:00
Qstick
6845eaa9b2 Re-enable some update tests 2023-01-21 13:03:48 -06:00
Qstick
c1e65874bc Bump version to 4.4.1 2023-01-21 11:43:48 -06:00
Bakerboy448
226a5da0c9 Fixed: Parse HDCAMRip as CAM 2023-01-21 11:41:37 -06:00
Qstick
685a24e476 Fixed: RemotePathMappingCheck Improvements 2023-01-16 22:45:55 -06:00
Qstick
cae4faae61 Fixed: DownloadClientRootFolderCheck Improvements 2023-01-16 22:38:05 -06:00
Qstick
5dac6badf2 Fixed: Ignore movie add errors during collection sync
Fixes #7982
2023-01-11 23:34:21 -06:00
Weblate
5948f56482 Translated using Weblate (Ukrainian) [skip ci]
Currently translated at 100.0% (1156 of 1156 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN)) [skip ci]

Currently translated at 100.0% (1156 of 1156 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1156 of 1156 strings)

Translated using Weblate (Russian) [skip ci]

Currently translated at 100.0% (1156 of 1156 strings)

Translated using Weblate (Dutch) [skip ci]

Currently translated at 95.5% (1105 of 1156 strings)

Translated using Weblate (Hungarian) [skip ci]

Currently translated at 100.0% (1156 of 1156 strings)

Translated using Weblate (Finnish) [skip ci]

Currently translated at 100.0% (1156 of 1156 strings)

Translated using Weblate (French) [skip ci]

Currently translated at 97.5% (1128 of 1156 strings)

Co-authored-by: AlexR-sf <omg.portal.supp@gmail.com>
Co-authored-by: Csaba <csab0825@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: aenron <1414004038@qq.com>
Co-authored-by: andrey4korop <andrey999@i.ua>
Co-authored-by: jjTogo228 <juniorbiam@gmail.com>
Co-authored-by: verhese <sean.verheyen1@telenet.be>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/uk/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/zh_CN/
Translation: Servarr/Radarr
2023-01-08 22:28:28 -06:00
Mark McDowall
98ddd0386b Fixed: Trakt connection auth tokens not being refreshed
Closes #7873

(cherry picked from commit d09e5d8eb4097cbba1ee0a668dbb27f941cc4f68)
2023-01-08 22:22:01 -06:00
Mark McDowall
2947b244e4 Fixed: Quality cutoff updating in UI when adding/removing qualities
Closes #7879

(cherry picked from commit fea66cb7bccc7e94523614db38b157fa38f55ea5)
2023-01-08 21:43:55 -06:00
Mark McDowall
72552b8084 New: Option to include movie image for Gotify notifications
Closes #7920

(cherry picked from commit e57e68c97a9d24f8344623ac8f731c2da220686b)
2023-01-08 21:41:08 -06:00
Qstick
09642444d7 Switch Trakt to STJson
Fixes #7913
2023-01-08 21:11:59 -06:00
Qstick
d1080b825c Fixed: Truncate custom format card tags
Fixes #7725
Fixes #7973

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2023-01-08 21:00:43 -06:00
Qstick
001421de10 New: Improve messaging for rejected quality upgrades
Fixes #7461

Co-Authored-By: bakerboy448 <55419169+bakerboy448@users.noreply.github.com>
2023-01-08 20:52:47 -06:00
Qstick
bab9b8b36a Add Volta node config
Fixes #7600
Fixes #7747

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2023-01-08 20:52:47 -06:00
Qstick
0fb738aa2e Fixed: Kodi Metadata Subtitle Language
Fixes #7962

Co-Authored-By: Stevie Robinson <stevie.robinson@gmail.com>
2023-01-08 20:52:47 -06:00
Mark McDowall
4963920a46 New: Added health check warning if SABnzbd sorting is enabled
(cherry picked from commit 61fa1e5e3f00072f0d5f59cc883fac74fe12ee9d)
2023-01-08 20:38:26 -06:00
Qstick
f0d10fe1cd Fixed: Correct messaging when release is not upgrade
Fixes #7963
2023-01-08 20:24:13 -06:00
James Hu
386b33b624 New: Include movie title and year when logging report
* Include movie title and year when logging report

* Change verbage

Co-authored-by: Robin Dadswell <19610103+RobinDadswell@users.noreply.github.com>
2023-01-05 21:07:59 +00:00
Mark McDowall
98201508f2 New: Description for indexer RSS setting
(cherry picked from commit 396406b2174c4876057175e7537a4718eee2abca)
2023-01-04 10:19:57 +00:00
Qstick
9723c569a1 Bump version to 4.4.0 2023-01-03 18:48:07 -06:00
Qstick
0584d7676c Bump FFProbe and Newtonsoft 2023-01-02 22:02:50 -06:00
Weblate
09c42530ec Translated using Weblate (Dutch) [skip ci]
Currently translated at 95.4% (1102 of 1155 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Translated using Weblate (Italian) [skip ci]

Currently translated at 94.3% (1090 of 1155 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN)) [skip ci]

Currently translated at 99.9% (1154 of 1155 strings)

Translated using Weblate (Ukrainian) [skip ci]

Currently translated at 94.4% (1091 of 1155 strings)

Co-authored-by: Davide Palma <github@davidepalma.it>
Co-authored-by: Iagocds <cdsiago@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: andrey4korop <andrey999@i.ua>
Co-authored-by: lhquark <lhquark@gmail.com>
Co-authored-by: vyruz1986 <alex.goris@fastlikehell.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/uk/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/zh_CN/
Translation: Servarr/Radarr
2023-01-02 21:47:27 -06:00
Mark McDowall
0697d694e0 New: Improved messaging when qBittorrent fails due to host header rejection
(cherry picked from commit 48b4cc5f3ffa0cb8eea6748db9091267216cef4f)
2023-01-02 21:46:10 -06:00
Mark McDowall
e085f6af8a Fixed: Multiple pushed releases will be processed sequentially
(cherry picked from commit 1f8e1cf582f59fe1e8dcc0fad15afeed6d9cd9d1)
2023-01-02 17:59:43 -06:00
Colin Gagnaire
7feda1c446 New: Add support for native Freebox Download Client
(cherry picked from commit fb76c237bfbb8aa43bcdd9ce34d90ea843011cee)
2022-12-27 21:20:33 +00:00
Winter
e1f83c205d Bump MonoTorrent to 2.0.7 2022-12-24 14:48:08 -06:00
Weblate
db00edd266 Translated using Weblate (Bengali) [skip ci]
Currently translated at 0.4% (5 of 1155 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN)) [skip ci]

Currently translated at 99.3% (1147 of 1155 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Translated using Weblate (Hungarian) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Translated using Weblate (German) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Translated using Weblate (Finnish) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Translated using Weblate (Dutch) [skip ci]

Currently translated at 95.4% (1102 of 1155 strings)

Translated using Weblate (Finnish) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Translated using Weblate (Italian) [skip ci]

Currently translated at 94.3% (1090 of 1155 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN)) [skip ci]

Currently translated at 99.3% (1148 of 1155 strings)

Translated using Weblate (Italian) [skip ci]

Currently translated at 93.9% (1085 of 1155 strings)

Translated using Weblate (Italian) [skip ci]

Currently translated at 93.9% (1085 of 1155 strings)

Co-authored-by: Csaba <csab0825@gmail.com>
Co-authored-by: Frank van den Bosch <frank@fbtech.nl>
Co-authored-by: Freelf <freelf.me@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Mipiaceanutella <remix-polity-0l@icloud.com>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: benniblot <ben2004engler@gmail.com>
Co-authored-by: deepserket <deepserket@gmail.com>
Co-authored-by: hidaba <nag@hidaba.com>
Co-authored-by: ningxia <xianing7105@163.com>
Co-authored-by: saambd <me@salimrahman.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/bn/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/zh_CN/
Translation: Servarr/Radarr
2022-12-22 20:34:42 -06:00
Mark McDowall
d699f61f5d Fixed: Prevent unexpected data breaking Series Import
(cherry picked from commit b8714d80a1ede761042ab469110edf552a74ac6b)
2022-12-22 20:33:30 -06:00
Mark McDowall
dc1b478f2c Fixed: Only log /proc/mounts exception once per process
(cherry picked from commit ce0388ca99b7f89bd9e8971777a7995c4361d268)
2022-12-22 20:32:43 -06:00
erri120
0ca665c903 New: Parse Open Matte as Edition
To make James Cameron happy.
2022-12-18 10:36:50 -06:00
Mark McDowall
111c6a743f New: Rename Emby to Emby / Jellyfin
(cherry picked from commit ee1ee8f267079e18015829065a76a628929cf4b2)
2022-12-17 18:18:52 +00:00
Qstick
d3517532a4 Update README for DigitalOcean attribution
[common]
2022-12-17 11:31:47 -06:00
Qstick
5790ebc558 Bump version to 4.3.2 2022-12-11 19:03:47 -06:00
Mark McDowall
c11f72c098 New: IPv6 support for connections/indexers/download clients
Closes #7850

(cherry picked from commit 1b90fbcf7df2c1086da4791c6491771924b1b7aa)
2022-12-10 12:05:55 -06:00
Mark McDowall
3617bef54b Fixed: Improve Bind Address validation and help text
Closes #7849

(cherry picked from commit 6bdeafcf8c78e145595f52e885356be1210abe91)
2022-12-10 12:04:04 -06:00
Zak Saunders
a5fb01f1e6 New: Auto theme option to match OS theme
Co-authored-by: Qstick <qstick@gmail.com>
(cherry picked from commit 4ca5a213fa0fc29ed93e7e31b080728d6fa7f1f3)
2022-12-09 22:11:03 -06:00
Qstick
fa6acb7497 Simplify X-Forwarded-For handling
This happens in asp.net middleware now

Co-Authored-By: ta264 <ta264@users.noreply.github.com>

(cherry picked from commit 16e2d130e6a2e7239bcfe92187a7f990f93eff00)
2022-12-09 22:05:50 -06:00
Qstick
904259df92 New: Improve IPAddress.IsLocal method
Co-Authored-By: ta264 <ta264@users.noreply.github.com>

(cherry picked from commit fd98a179ab6fed8037c99344b34593aac24a0ac0)
2022-12-09 22:05:32 -06:00
Qstick
65c316bd6d Fixed: Smb paths fail on Kodi update
Fixes #7854
2022-12-09 22:04:50 -06:00
Qstick
3b46a08606 Fix PendingRelease Tests 2022-12-05 22:12:34 -06:00
Servarr
6ad49373d4 Automated API Docs update 2022-12-05 21:52:13 -06:00
Qstick
2a1f57c085 Fixed: Sending Webhook on upgrade if media info is unavailable
Fixes #7838

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2022-12-05 21:34:00 -06:00
Qstick
9d9065fbcd API Updates
Fixes #7833
Fixes #6785
Fixes #6787

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2022-12-05 21:34:00 -06:00
Qstick
694940452c Fixed: Loading queue when there are pending items that were added before upgrading
Fixes #7823

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2022-12-05 21:34:00 -06:00
Qstick
f5d6a79998 Fixed: Grab/remove queue actions not showing spinner
Fixes #7821
2022-12-05 21:34:00 -06:00
Qstick
4cc98a10a0 Fixed: Use route Id for PUT requests if not passed in body
Fixes #7809
2022-12-05 21:34:00 -06:00
Qstick
1751bd1a58 Fixed: Correct Attribute compare for Id validation
(cherry picked from commit 7e48ea0231272ae56c30f5f43339f0dca7a27fb3)
2022-12-05 21:20:31 -06:00
151 changed files with 4533 additions and 2175 deletions

23
.gitignore vendored
View File

@@ -166,27 +166,8 @@ packages.config.md5sum
# Common IntelliJ Platform excludes
# User specific
**/.idea/**/workspace.xml
**/.idea/**/tasks.xml
**/.idea/shelf/*
**/.idea/dictionaries
**/.idea/.idea.Radarr.Posix
**/.idea/.idea.Radarr.Windows
# Sensitive or high-churn files
**/.idea/**/dataSources/
**/.idea/**/dataSources.ids
**/.idea/**/dataSources.xml
**/.idea/**/dataSources.local.xml
**/.idea/**/sqlDataSources.xml
**/.idea/**/dynamic.xml
# Rider
# Rider auto-generates .iml files, and contentModel.xml
**/.idea/**/*.iml
**/.idea/**/contentModel.xml
**/.idea/**/modules.xml
# Ignore Rider projects completely for now
.idea/
# ignore node_modules symlink
node_modules

View File

@@ -76,6 +76,15 @@ Thank you to [<img src="/Logo/jetbrains.svg" alt="JetBrains" width="32"> JetBrai
* [<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/)
## DigitalOcean
This project is also supported by DigitalOcean
<p>
<a href="https://www.digitalocean.com/">
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
</a>
</p>
### License
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)

View File

@@ -9,7 +9,7 @@ variables:
testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '4.3.1'
majorVersion: '4.4.1'
minorVersion: $[counter('minorVersion', 2000)]
radarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(radarrVersion)'

View File

@@ -75,13 +75,23 @@ class Queue extends Component {
return;
}
const nextState = {};
if (prevProps.items !== items) {
nextState.items = items;
}
const selectedIds = this.getSelectedIds();
const isPendingSelected = _.some(this.props.items, (item) => {
return selectedIds.indexOf(item.id) > -1 && item.status === 'delay';
});
if (isPendingSelected !== this.state.isPendingSelected) {
this.setState({ isPendingSelected });
nextState.isPendingSelected = isPendingSelected;
}
if (!_.isEmpty(nextState)) {
this.setState(nextState);
}
}
@@ -214,26 +224,29 @@ class Queue extends Component {
<PageContentBody>
{
isRefreshing && !isAllPopulated &&
<LoadingIndicator />
isRefreshing && !isAllPopulated ?
<LoadingIndicator /> :
null
}
{
!isRefreshing && hasError &&
!isRefreshing && hasError ?
<div>
{translate('FailedToLoadQueue')}
</div>
</div> :
null
}
{
isAllPopulated && !hasError && !items.length &&
isAllPopulated && !hasError && !items.length ?
<div>
{translate('QueueIsEmpty')}
</div>
</div> :
null
}
{
isAllPopulated && !hasError && !!items.length &&
isAllPopulated && !hasError && !!items.length ?
<div>
<Table
columns={columns}
@@ -268,7 +281,8 @@ class Queue extends Component {
isFetching={isRefreshing}
{...otherProps}
/>
</div>
</div> :
null
}
</PageContentBody>

View File

@@ -225,13 +225,19 @@ class ImportMovieFooter extends Component {
body={
<ul>
{
importError.responseJSON.map((error, index) => {
return (
<li key={index}>
{error.errorMessage}
</li>
);
})
Array.isArray(importError.responseJSON) ?
importError.responseJSON.map((error, index) => {
return (
<li key={index}>
{error.errorMessage}
</li>
);
}) :
<li>
{
JSON.stringify(importError.responseJSON)
}
</li>
}
</ul>
}

View File

@@ -152,13 +152,19 @@ class ImportMovieSelectFolder extends Component {
<ul>
{
saveError.responseJSON.map((e, index) => {
return (
<li key={index}>
{e.errorMessage}
</li>
);
})
Array.isArray(saveError.responseJSON) ?
saveError.responseJSON.map((e, index) => {
return (
<li key={index}>
{e.errorMessage}
</li>
);
}) :
<li>
{
JSON.stringify(saveError.responseJSON)
}
</li>
}
</ul>
</Alert> :

View File

@@ -113,10 +113,12 @@ class EnhancedSelectInput extends Component {
this._scheduleUpdate();
}
if (!Array.isArray(this.props.value) && prevProps.value !== this.props.value) {
this.setState({
selectedIndex: getSelectedIndex(this.props)
});
if (!Array.isArray(this.props.value)) {
if (prevProps.value !== this.props.value || prevProps.values !== this.props.values) {
this.setState({
selectedIndex: getSelectedIndex(this.props)
});
}
}
}
@@ -332,6 +334,11 @@ class EnhancedSelectInput extends Component {
const isMultiSelect = Array.isArray(value);
const selectedOption = getSelectedOption(selectedIndex, values);
let selectedValue = value;
if (!values.length) {
selectedValue = isMultiSelect ? [] : '';
}
return (
<div>
@@ -372,15 +379,17 @@ class EnhancedSelectInput extends Component {
onPress={this.onPress}
>
{
isFetching &&
isFetching ?
<LoadingIndicator
className={styles.loading}
size={20}
/>
/> :
null
}
{
!isFetching &&
isFetching ?
null :
<Icon
name={icons.CARET_DOWN}
/>
@@ -400,7 +409,7 @@ class EnhancedSelectInput extends Component {
onPress={this.onPress}
>
<SelectedValueComponent
value={value}
value={selectedValue}
values={values}
{...selectedValueOptions}
{...selectedOption}
@@ -418,15 +427,17 @@ class EnhancedSelectInput extends Component {
>
{
isFetching &&
isFetching ?
<LoadingIndicator
className={styles.loading}
size={20}
/>
/> :
null
}
{
!isFetching &&
isFetching ?
null :
<Icon
name={icons.CARET_DOWN}
/>
@@ -506,7 +517,7 @@ class EnhancedSelectInput extends Component {
</Manager>
{
isMobile &&
isMobile ?
<Modal
className={styles.optionsModal}
size={sizes.EXTRA_SMALL}
@@ -557,7 +568,8 @@ class EnhancedSelectInput extends Component {
}
</Scroller>
</ModalBody>
</Modal>
</Modal> :
null
}
</div>
);

View File

@@ -24,7 +24,7 @@ function HintedSelectInputSelectedValue(props) {
>
<div className={styles.valueText}>
{
isMultiSelect &&
isMultiSelect ?
value.map((key, index) => {
const v = valuesMap[key];
return (
@@ -32,26 +32,28 @@ function HintedSelectInputSelectedValue(props) {
{v ? v.value : key}
</Label>
);
})
}) :
null
}
{
!isMultiSelect && value
isMultiSelect ? null : value
}
</div>
{
hint != null && includeHint &&
hint != null && includeHint ?
<div className={styles.hintText}>
{hint}
</div>
</div> :
null
}
</EnhancedSelectInputSelectedValue>
);
}
HintedSelectInputSelectedValue.propTypes = {
value: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number]))]).isRequired,
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string, PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number]))]).isRequired,
values: PropTypes.arrayOf(PropTypes.object).isRequired,
hint: PropTypes.string,
isMultiSelect: PropTypes.bool.isRequired,

View File

@@ -68,7 +68,7 @@ RootFolderSelectInputOption.propTypes = {
value: PropTypes.string.isRequired,
freeSpace: PropTypes.number,
movieFolder: PropTypes.string,
isMissing: PropTypes.boolean,
isMissing: PropTypes.bool,
isMobile: PropTypes.bool.isRequired,
isWindows: PropTypes.bool
};

View File

@@ -60,7 +60,7 @@ class FilterMenu extends Component {
iconName={icons.FILTER}
text={translate('Filter')}
isDisabled={isDisabled}
indicator={selectedFilterKey !== 'all'}
showIndicator={selectedFilterKey !== 'all'}
/>
<FilterMenuContent

View File

@@ -1,11 +1,19 @@
.menuButton {
composes: menuButton from '~./MenuButton.css';
position: relative;
&:hover {
color: #666;
}
}
.indicatorContainer {
position: absolute;
top: 10px;
left: 10px;
}
.label {
margin-left: 5px;
}

View File

@@ -1,13 +1,15 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import Icon from 'Components/Icon';
import MenuButton from 'Components/Menu/MenuButton';
import { icons } from 'Helpers/Props';
import styles from './PageMenuButton.css';
function PageMenuButton(props) {
const {
iconName,
indicator,
showIndicator,
text,
...otherProps
} = props;
@@ -22,6 +24,22 @@ function PageMenuButton(props) {
size={18}
/>
{
showIndicator ?
<span
className={classNames(
styles.indicatorContainer,
'fa-layers fa-fw'
)}
>
<Icon
name={icons.CIRCLE}
size={9}
/>
</span> :
null
}
<div className={styles.label}>
{text}
</div>
@@ -32,11 +50,11 @@ function PageMenuButton(props) {
PageMenuButton.propTypes = {
iconName: PropTypes.object.isRequired,
text: PropTypes.string,
indicator: PropTypes.bool.isRequired
showIndicator: PropTypes.bool.isRequired
};
PageMenuButton.defaultProps = {
indicator: false
showIndicator: false
};
export default PageMenuButton;

View File

@@ -9,7 +9,7 @@ import styles from './ToolbarMenuButton.css';
function ToolbarMenuButton(props) {
const {
iconName,
indicator,
showIndicator,
text,
...otherProps
} = props;
@@ -26,7 +26,7 @@ function ToolbarMenuButton(props) {
/>
{
indicator &&
showIndicator &&
<span
className={classNames(
styles.indicatorContainer,
@@ -53,11 +53,11 @@ function ToolbarMenuButton(props) {
ToolbarMenuButton.propTypes = {
iconName: PropTypes.object.isRequired,
text: PropTypes.string,
indicator: PropTypes.bool.isRequired
showIndicator: PropTypes.bool.isRequired
};
ToolbarMenuButton.defaultProps = {
indicator: false
showIndicator: false
};
export default ToolbarMenuButton;

View File

@@ -20,7 +20,11 @@
.frontTextContainer {
z-index: 1;
color: var(--white);
color: var(--progressBarFrontTextColor);
}
.backTextContainer {
color: var(--progressBarBackTextColor);
}
.backTextContainer,

View File

@@ -17,6 +17,10 @@
font-size: 24px;
}
.buttons {
flex: 0 0 auto;
}
.cloneButton {
composes: button from '~Components/Link/IconButton.css';
@@ -36,3 +40,10 @@
margin: 0;
border: none;
}
.label {
@add-mixin truncate;
composes: label from '~Components/Label.css';
max-width: 100%;
}

View File

@@ -90,7 +90,7 @@ class CustomFormat extends Component {
{name}
</div>
<div>
<div className={styles.buttons}>
<IconButton
className={styles.cloneButton}
title={translate('CloneCustomFormat')}
@@ -124,6 +124,7 @@ class CustomFormat extends Component {
return (
<Label
className={styles.label}
key={index}
kind={kind}
>

View File

@@ -89,6 +89,7 @@ function EditIndexerModalContent(props) {
<FormInputGroup
type={inputTypes.CHECK}
name="enableRss"
helpText={supportsRss.value ? translate('RSSHelpText') : undefined}
helpTextWarning={supportsRss.value ? undefined : translate('RSSIsNotSupportedWithThisIndexer')}
isDisabled={!supportsRss.value}
{...enableRss}

View File

@@ -30,6 +30,7 @@
code {
padding: 0 1px;
border: 1px solid var(--borderColor);
background-color: #f7f7f7;
background-color: var(--modalCloseButtonHoverColor);
color: var(--movieBackgroundColor);
}
}

View File

@@ -7,11 +7,11 @@
&:hover {
.token {
background-color: #ddd;
background-color: var(--popoverTitleBackgroundInverseColor);
}
.example {
background-color: #ccc;
background-color: var(--popoverTitleBorderInverseColor);
}
}
}
@@ -27,7 +27,7 @@
.token {
flex: 0 0 50%;
padding: 6px 16px;
background-color: #eee;
background-color: var(--popoverTitleBorderColor);
font-family: $monoSpaceFontFamily;
}
@@ -38,7 +38,7 @@
justify-content: space-between;
flex: 0 0 50%;
padding: 6px 16px;
background-color: #ddd;
background-color: var(--popoverTitleBackgroundColor);
.footNote {
padding: 2px;

View File

@@ -201,6 +201,11 @@ export const defaultState = {
return genreList.sort(sortByName);
}
},
{
name: 'customFormatScore',
label: translate('CustomFormatScore'),
type: filterBuilderTypes.NUMBER
},
{
name: 'rejectionCount',
label: translate('RejectionCount'),

View File

@@ -226,6 +226,8 @@ module.exports = {
//
// Misc
progressBarFrontTextColor: white,
progressBarBackTextColor: white,
progressBarBackgroundColor: '#727070',
logEventsBackgroundColor: '#2a2a2a'
};

View File

@@ -1,7 +1,11 @@
import * as dark from './dark';
import * as light from './light';
const defaultDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const auto = defaultDark ? { ...dark } : { ...light };
export default {
auto,
light,
dark
};

View File

@@ -198,8 +198,8 @@ module.exports = {
popoverShadowColor: 'rgba(0, 0, 0, 0.2)',
popoverArrowBorderColor: '#fff',
popoverTitleBackgroundInverseColor: '#595959',
popoverTitleBorderInverseColor: '#707070',
popoverTitleBackgroundInverseColor: '#9b9b9b',
popoverTitleBorderInverseColor: '#bfbfbf',
popoverShadowInverseColor: 'rgba(0, 0, 0, 0.2)',
popoverArrowBorderInverseColor: 'rgba(58, 63, 81, 0.75)',
@@ -227,6 +227,8 @@ module.exports = {
//
// Misc
progressBarBackgroundColor: '#fff',
logEventsBackgroundColor: '#fff'
progressBarFrontTextColor: white,
progressBarBackTextColor: darkGray,
progressBarBackgroundColor: white,
logEventsBackgroundColor: white
};

View File

@@ -113,7 +113,7 @@
"file-loader": "6.2.0",
"filemanager-webpack-plugin": "5.0.0",
"html-webpack-plugin": "5.3.1",
"loader-utils": "^2.0.0",
"loader-utils": "^3.2.1",
"mini-css-extract-plugin": "1.5.0",
"postcss": "8.2.12",
"postcss-color-function": "4.1.0",
@@ -134,5 +134,9 @@
"webpack-cli": "4.9.1",
"webpack-livereload-plugin": "3.0.2",
"worker-loader": "3.0.8"
},
"volta": {
"node": "16.17.0",
"yarn": "1.22.19"
}
}

View File

@@ -0,0 +1,25 @@
using System.Globalization;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Common.Test.ExtensionTests.StringExtensionTests
{
[TestFixture]
public class IsValidIPAddressFixture
{
[TestCase("192.168.0.1")]
[TestCase("::1")]
[TestCase("2001:db8:4006:812::200e")]
public void should_validate_ip_address(string input)
{
input.IsValidIpAddress().Should().BeTrue();
}
[TestCase("sonarr.tv")]
public void should_not_parse_non_ip_address(string input)
{
input.IsValidIpAddress().Should().BeFalse();
}
}
}

View File

@@ -1,4 +1,4 @@
using FluentAssertions;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.Http;
using NzbDrone.Test.Common;
@@ -10,6 +10,7 @@ namespace NzbDrone.Common.Test.Http
[TestCase("abc://my_host.com:8080/root/api/")]
[TestCase("abc://my_host.com:8080//root/api/")]
[TestCase("abc://my_host.com:8080/root//api/")]
[TestCase("abc://[::1]:8080/root//api/")]
public void should_parse(string uri)
{
var newUri = new HttpUri(uri);

View File

@@ -7,34 +7,50 @@ namespace NzbDrone.Common.Extensions
{
public static bool IsLocalAddress(this IPAddress ipAddress)
{
if (ipAddress.IsIPv6LinkLocal)
// Map back to IPv4 if mapped to IPv6, for example "::ffff:1.2.3.4" to "1.2.3.4".
if (ipAddress.IsIPv4MappedToIPv6)
{
return true;
ipAddress = ipAddress.MapToIPv4();
}
// Checks loopback ranges for both IPv4 and IPv6.
if (IPAddress.IsLoopback(ipAddress))
{
return true;
}
// IPv4
if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
{
byte[] bytes = ipAddress.GetAddressBytes();
switch (bytes[0])
{
case 10:
case 127:
return true;
case 172:
return bytes[1] < 32 && bytes[1] >= 16;
case 192:
return bytes[1] == 168;
default:
return false;
}
return IsLocalIPv4(ipAddress.GetAddressBytes());
}
// IPv6
if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
return ipAddress.IsIPv6LinkLocal ||
ipAddress.IsIPv6UniqueLocal ||
ipAddress.IsIPv6SiteLocal;
}
return false;
}
private static bool IsLocalIPv4(byte[] ipv4Bytes)
{
// Link local (no IP assigned by DHCP): 169.254.0.0 to 169.254.255.255 (169.254.0.0/16)
bool IsLinkLocal() => ipv4Bytes[0] == 169 && ipv4Bytes[1] == 254;
// Class A private range: 10.0.0.0 10.255.255.255 (10.0.0.0/8)
bool IsClassA() => ipv4Bytes[0] == 10;
// Class B private range: 172.16.0.0 172.31.255.255 (172.16.0.0/12)
bool IsClassB() => ipv4Bytes[0] == 172 && ipv4Bytes[1] >= 16 && ipv4Bytes[1] <= 31;
// Class C private range: 192.168.0.0 192.168.255.255 (192.168.0.0/16)
bool IsClassC() => ipv4Bytes[0] == 192 && ipv4Bytes[1] == 168;
return IsLinkLocal() || IsClassA() || IsClassC() || IsClassB();
}
}
}

View File

@@ -2,6 +2,8 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
@@ -192,5 +194,30 @@ namespace NzbDrone.Common.Extensions
.Replace("'", "%27")
.Replace("%7E", "~");
}
public static bool IsValidIpAddress(this string value)
{
if (!IPAddress.TryParse(value, out var parsedAddress))
{
return false;
}
if (parsedAddress.Equals(IPAddress.Parse("255.255.255.255")))
{
return false;
}
if (parsedAddress.IsIPv6Multicast)
{
return false;
}
return parsedAddress.AddressFamily == AddressFamily.InterNetwork || parsedAddress.AddressFamily == AddressFamily.InterNetworkV6;
}
public static string ToUrlHost(this string input)
{
return input.Contains(":") ? $"[{input}]" : input;
}
}
}

View File

@@ -8,7 +8,7 @@ namespace NzbDrone.Common.Http
{
public class HttpUri : IEquatable<HttpUri>
{
private static readonly Regex RegexUri = new Regex(@"^(?:(?<scheme>[a-z]+):)?(?://(?<host>[-_A-Z0-9.]+)(?::(?<port>[0-9]{1,5}))?)?(?<path>(?:(?:(?<=^)|/+)[^/?#\r\n]+)+/*|/+)?(?:\?(?<query>[^#\r\n]*))?(?:\#(?<fragment>.*))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex RegexUri = new Regex(@"^(?:(?<scheme>[a-z]+):)?(?://(?<host>[-_A-Z0-9.]+|\[[[A-F0-9:]+\])(?::(?<port>[0-9]{1,5}))?)?(?<path>(?:(?:(?<=^)|/+)[^/?#\r\n]+)+/*|/+)?(?:\?(?<query>[^#\r\n]*))?(?:\#(?<fragment>.*))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly string _uri;
public string FullUri => _uri;
@@ -70,6 +70,8 @@ namespace NzbDrone.Common.Http
private void Parse()
{
var parseSuccess = Uri.TryCreate(_uri, UriKind.RelativeOrAbsolute, out var uri);
var match = RegexUri.Match(_uri);
var scheme = match.Groups["scheme"];
@@ -79,7 +81,7 @@ namespace NzbDrone.Common.Http
var query = match.Groups["query"];
var fragment = match.Groups["fragment"];
if (!match.Success || (scheme.Success && !host.Success && path.Success))
if (!parseSuccess || (scheme.Success && !host.Success && path.Success))
{
throw new ArgumentException("Uri didn't match expected pattern: " + _uri);
}

View File

@@ -8,6 +8,7 @@ using System.Threading;
using NLog;
using NLog.Common;
using NLog.Targets;
using Npgsql;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions;
using Sentry;
@@ -34,6 +35,14 @@ namespace NzbDrone.Common.Instrumentation.Sentry
SQLiteErrorCode.Auth
};
private static readonly HashSet<string> FilteredPostgresErrorCodes = new HashSet<string>
{
PostgresErrorCodes.OutOfMemory,
PostgresErrorCodes.TooManyConnections,
PostgresErrorCodes.DiskFull,
PostgresErrorCodes.ProgramLimitExceeded
};
// use string and not Type so we don't need a reference to the project
// where these are defined
private static readonly HashSet<string> FilteredExceptionTypeNames = new HashSet<string>
@@ -250,6 +259,19 @@ namespace NzbDrone.Common.Instrumentation.Sentry
isSentry = false;
}
var pgEx = logEvent.Exception as PostgresException;
if (pgEx != null && FilteredPostgresErrorCodes.Contains(pgEx.SqlState))
{
return false;
}
// We don't care about transient network and timeout errors
var npgEx = logEvent.Exception as NpgsqlException;
if (npgEx != null && npgEx.IsTransient)
{
return false;
}
if (FilteredExceptionTypeNames.Contains(ex.GetType().Name))
{
isSentry = false;

View File

@@ -7,9 +7,10 @@
<PackageReference Include="DryIoc.dll" Version="5.3.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="NLog" Version="5.0.1" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.0.0" />
<PackageReference Include="Npgsql" Version="5.0.11" />
<PackageReference Include="Sentry" Version="3.23.1" />
<PackageReference Include="NLog.Targets.Syslog" Version="7.0.0" />
<PackageReference Include="SharpZipLib" Version="1.3.3" />

View File

@@ -0,0 +1,209 @@
using System.Collections.Generic;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.CustomFormats;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.DecisionEngineTests
{
[TestFixture]
public class UpgradeAllowedSpecificationFixture : CoreTest<UpgradableSpecification>
{
private CustomFormat _customFormatOne;
private CustomFormat _customFormatTwo;
private Profile _qualityProfile;
[SetUp]
public void Setup()
{
_customFormatOne = new CustomFormat
{
Id = 1,
Name = "One"
};
_customFormatTwo = new CustomFormat
{
Id = 2,
Name = "Two"
};
_qualityProfile = new Profile
{
Cutoff = Quality.Bluray1080p.Id,
Items = Qualities.QualityFixture.GetDefaultQualities(),
UpgradeAllowed = false,
CutoffFormatScore = 100,
FormatItems = new List<ProfileFormatItem>
{
new ProfileFormatItem
{
Format = _customFormatOne,
Score = 50
},
new ProfileFormatItem
{
Format = _customFormatTwo,
Score = 100
}
}
};
}
[Test]
public void should_return_false_when_quality_is_better_custom_formats_are_the_same_and_upgrading_is_not_allowed()
{
_qualityProfile.UpgradeAllowed = false;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat>(),
new QualityModel(Quality.Bluray1080p),
new List<CustomFormat>())
.Should().BeFalse();
}
[Test]
public void should_return_false_when_quality_is_same_and_custom_format_is_upgrade_and_upgrading_is_not_allowed()
{
_qualityProfile.UpgradeAllowed = false;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatOne },
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatTwo })
.Should().BeFalse();
}
[Test]
public void should_return_true_for_custom_format_upgrade_when_upgrading_is_allowed()
{
_qualityProfile.UpgradeAllowed = true;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatOne },
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatTwo })
.Should().BeTrue();
}
[Test]
public void should_return_true_for_same_custom_format_score_when_upgrading_is_not_allowed()
{
_qualityProfile.UpgradeAllowed = false;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatOne },
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatOne })
.Should().BeTrue();
}
[Test]
public void should_return_true_for_lower_custom_format_score_when_upgrading_is_allowed()
{
_qualityProfile.UpgradeAllowed = true;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatTwo },
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatOne })
.Should().BeTrue();
}
[Test]
public void should_return_true_for_lower_language_when_upgrading_is_not_allowed()
{
_qualityProfile.UpgradeAllowed = false;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatTwo },
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatOne })
.Should().BeTrue();
}
[Test]
public void should_return_true_for_quality_upgrade_when_upgrading_is_allowed()
{
_qualityProfile.UpgradeAllowed = true;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat>(),
new QualityModel(Quality.Bluray1080p),
new List<CustomFormat>())
.Should().BeTrue();
}
[Test]
public void should_return_true_for_same_quality_when_upgrading_is_allowed()
{
_qualityProfile.UpgradeAllowed = true;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat>(),
new QualityModel(Quality.DVD),
new List<CustomFormat>())
.Should().BeTrue();
}
[Test]
public void should_return_true_for_same_quality_when_upgrading_is_not_allowed()
{
_qualityProfile.UpgradeAllowed = false;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat>(),
new QualityModel(Quality.DVD),
new List<CustomFormat>())
.Should().BeTrue();
}
[Test]
public void should_return_true_for_lower_quality_when_upgrading_is_allowed()
{
_qualityProfile.UpgradeAllowed = true;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat>(),
new QualityModel(Quality.SDTV),
new List<CustomFormat>())
.Should().BeTrue();
}
[Test]
public void should_return_true_for_lower_quality_when_upgrading_is_not_allowed()
{
_qualityProfile.UpgradeAllowed = false;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat>(),
new QualityModel(Quality.SDTV),
new List<CustomFormat>())
.Should().BeTrue();
}
}
}

View File

@@ -0,0 +1,367 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Http;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.Download.Clients.FreeboxDownload;
using NzbDrone.Core.Download.Clients.FreeboxDownload.Responses;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Test.Download.DownloadClientTests.FreeboxDownloadTests
{
[TestFixture]
public class TorrentFreeboxDownloadFixture : DownloadClientFixtureBase<TorrentFreeboxDownload>
{
protected FreeboxDownloadSettings _settings;
protected FreeboxDownloadConfiguration _downloadConfiguration;
protected FreeboxDownloadTask _task;
protected string _defaultDestination = @"/some/path";
protected string _encodedDefaultDestination = "L3NvbWUvcGF0aA==";
protected string _category = "somecat";
protected string _encodedDefaultDestinationAndCategory = "L3NvbWUvcGF0aC9zb21lY2F0";
protected string _destinationDirectory = @"/path/to/media";
protected string _encodedDestinationDirectory = "L3BhdGgvdG8vbWVkaWE=";
protected OsPath _physicalPath = new OsPath("/mnt/sdb1/mydata");
protected string _downloadURL => "magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcad53426&dn=download";
[SetUp]
public void Setup()
{
Subject.Definition = new DownloadClientDefinition();
_settings = new FreeboxDownloadSettings()
{
Host = "127.0.0.1",
Port = 443,
ApiUrl = "/api/v1/",
AppId = "someid",
AppToken = "S0mEv3RY1oN9T0k3n"
};
Subject.Definition.Settings = _settings;
_downloadConfiguration = new FreeboxDownloadConfiguration()
{
DownloadDirectory = _encodedDefaultDestination
};
_task = new FreeboxDownloadTask()
{
Id = "id0",
Name = "name",
DownloadDirectory = "L3NvbWUvcGF0aA==",
InfoHash = "HASH",
QueuePosition = 1,
Status = FreeboxDownloadTaskStatus.Unknown,
Eta = 0,
Error = "none",
Type = FreeboxDownloadTaskType.Bt.ToString(),
IoPriority = FreeboxDownloadTaskIoPriority.Normal.ToString(),
StopRatio = 150,
PieceLength = 125,
CreatedTimestamp = 1665261599,
Size = 1000,
ReceivedPrct = 0,
ReceivedBytes = 0,
ReceivedRate = 0,
TransmittedPrct = 0,
TransmittedBytes = 0,
TransmittedRate = 0,
};
Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[0]));
}
protected void GivenCategory()
{
_settings.Category = _category;
}
protected void GivenDestinationDirectory()
{
_settings.DestinationDirectory = _destinationDirectory;
}
protected virtual void GivenDownloadConfiguration()
{
Mocker.GetMock<IFreeboxDownloadProxy>()
.Setup(s => s.GetDownloadConfiguration(It.IsAny<FreeboxDownloadSettings>()))
.Returns(_downloadConfiguration);
}
protected virtual void GivenTasks(List<FreeboxDownloadTask> torrents)
{
if (torrents == null)
{
torrents = new List<FreeboxDownloadTask>();
}
Mocker.GetMock<IFreeboxDownloadProxy>()
.Setup(s => s.GetTasks(It.IsAny<FreeboxDownloadSettings>()))
.Returns(torrents);
}
protected void PrepareClientToReturnQueuedItem()
{
_task.Status = FreeboxDownloadTaskStatus.Queued;
GivenTasks(new List<FreeboxDownloadTask>
{
_task
});
}
protected void GivenSuccessfulDownload()
{
Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[1000]));
Mocker.GetMock<IFreeboxDownloadProxy>()
.Setup(s => s.AddTaskFromUrl(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()))
.Callback(PrepareClientToReturnQueuedItem);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Setup(s => s.AddTaskFromFile(It.IsAny<string>(), It.IsAny<byte[]>(), It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()))
.Callback(PrepareClientToReturnQueuedItem);
}
protected override RemoteMovie CreateRemoteMovie()
{
var movie = base.CreateRemoteMovie();
movie.Release.DownloadUrl = _downloadURL;
return movie;
}
[Test]
public void Download_with_DestinationDirectory_should_force_directory()
{
GivenDestinationDirectory();
GivenSuccessfulDownload();
var remoteMovie = CreateRemoteMovie();
Subject.Download(remoteMovie);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), _encodedDestinationDirectory, It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()), Times.Once());
}
[Test]
public void Download_with_Category_should_force_directory()
{
GivenDownloadConfiguration();
GivenCategory();
GivenSuccessfulDownload();
var remoteMovie = CreateRemoteMovie();
Subject.Download(remoteMovie);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), _encodedDefaultDestinationAndCategory, It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()), Times.Once());
}
[Test]
public void Download_without_DestinationDirectory_and_Category_should_use_default()
{
GivenDownloadConfiguration();
GivenSuccessfulDownload();
var remoteMovie = CreateRemoteMovie();
Subject.Download(remoteMovie);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), _encodedDefaultDestination, It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()), Times.Once());
}
[TestCase(false, false)]
[TestCase(true, true)]
public void Download_should_pause_torrent_as_expected(bool addPausedSetting, bool toBePausedFlag)
{
_settings.AddPaused = addPausedSetting;
GivenDownloadConfiguration();
GivenSuccessfulDownload();
var remoteMovie = CreateRemoteMovie();
Subject.Download(remoteMovie);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), It.IsAny<string>(), toBePausedFlag, It.IsAny<bool>(), It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()), Times.Once());
}
[TestCase(0, (int)FreeboxDownloadPriority.First, (int)FreeboxDownloadPriority.First, true)]
[TestCase(0, (int)FreeboxDownloadPriority.Last, (int)FreeboxDownloadPriority.First, true)]
[TestCase(0, (int)FreeboxDownloadPriority.First, (int)FreeboxDownloadPriority.Last, false)]
[TestCase(0, (int)FreeboxDownloadPriority.Last, (int)FreeboxDownloadPriority.Last, false)]
[TestCase(22, (int)FreeboxDownloadPriority.First, (int)FreeboxDownloadPriority.First, true)]
[TestCase(22, (int)FreeboxDownloadPriority.Last, (int)FreeboxDownloadPriority.First, false)]
[TestCase(22, (int)FreeboxDownloadPriority.First, (int)FreeboxDownloadPriority.Last, true)]
[TestCase(22, (int)FreeboxDownloadPriority.Last, (int)FreeboxDownloadPriority.Last, false)]
public void Download_should_queue_torrent_first_as_expected(int ageDay, int olderPriority, int recentPriority, bool toBeQueuedFirstFlag)
{
_settings.OlderPriority = olderPriority;
_settings.RecentPriority = recentPriority;
GivenDownloadConfiguration();
GivenSuccessfulDownload();
var remoteMovie = CreateRemoteMovie();
remoteMovie.Movie.MovieMetadata.Value.PhysicalRelease = DateTime.UtcNow.AddDays(-ageDay);
Subject.Download(remoteMovie);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>(), toBeQueuedFirstFlag, It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()), Times.Once());
}
[TestCase(0, 0)]
[TestCase(1.5, 150)]
public void Download_should_define_seed_ratio_as_expected(double? providerSeedRatio, double? expectedSeedRatio)
{
GivenDownloadConfiguration();
GivenSuccessfulDownload();
var remoteMovie = CreateRemoteMovie();
remoteMovie.SeedConfiguration = new TorrentSeedConfiguration();
remoteMovie.SeedConfiguration.Ratio = providerSeedRatio;
Subject.Download(remoteMovie);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<bool>(), expectedSeedRatio, It.IsAny<FreeboxDownloadSettings>()), Times.Once());
}
[Test]
public void GetItems_should_return_empty_list_if_no_tasks_available()
{
GivenTasks(new List<FreeboxDownloadTask>());
Subject.GetItems().Should().BeEmpty();
}
[Test]
public void GetItems_should_return_ignore_tasks_of_unknown_type()
{
_task.Status = FreeboxDownloadTaskStatus.Done;
_task.Type = "toto";
GivenTasks(new List<FreeboxDownloadTask> { _task });
Subject.GetItems().Should().BeEmpty();
}
[Test]
public void GetItems_when_destinationdirectory_is_set_should_ignore_downloads_in_wrong_folder()
{
_settings.DestinationDirectory = @"/some/path/that/will/not/match";
_task.Status = FreeboxDownloadTaskStatus.Done;
GivenTasks(new List<FreeboxDownloadTask> { _task });
Subject.GetItems().Should().BeEmpty();
}
[Test]
public void GetItems_when_category_is_set_should_ignore_downloads_in_wrong_folder()
{
_settings.Category = "somecategory";
_task.Status = FreeboxDownloadTaskStatus.Done;
GivenTasks(new List<FreeboxDownloadTask> { _task });
Subject.GetItems().Should().BeEmpty();
}
[TestCase(FreeboxDownloadTaskStatus.Downloading, false, false)]
[TestCase(FreeboxDownloadTaskStatus.Done, true, true)]
[TestCase(FreeboxDownloadTaskStatus.Seeding, false, false)]
[TestCase(FreeboxDownloadTaskStatus.Stopped, false, false)]
public void GetItems_should_return_canBeMoved_and_canBeDeleted_as_expected(FreeboxDownloadTaskStatus apiStatus, bool canMoveFilesExpected, bool canBeRemovedExpected)
{
_task.Status = apiStatus;
GivenTasks(new List<FreeboxDownloadTask>() { _task });
var items = Subject.GetItems();
items.Should().HaveCount(1);
items.First().CanBeRemoved.Should().Be(canBeRemovedExpected);
items.First().CanMoveFiles.Should().Be(canMoveFilesExpected);
}
[TestCase(FreeboxDownloadTaskStatus.Stopped, DownloadItemStatus.Paused)]
[TestCase(FreeboxDownloadTaskStatus.Stopping, DownloadItemStatus.Paused)]
[TestCase(FreeboxDownloadTaskStatus.Queued, DownloadItemStatus.Queued)]
[TestCase(FreeboxDownloadTaskStatus.Starting, DownloadItemStatus.Downloading)]
[TestCase(FreeboxDownloadTaskStatus.Downloading, DownloadItemStatus.Downloading)]
[TestCase(FreeboxDownloadTaskStatus.Retry, DownloadItemStatus.Downloading)]
[TestCase(FreeboxDownloadTaskStatus.Checking, DownloadItemStatus.Downloading)]
[TestCase(FreeboxDownloadTaskStatus.Error, DownloadItemStatus.Warning)]
[TestCase(FreeboxDownloadTaskStatus.Seeding, DownloadItemStatus.Completed)]
[TestCase(FreeboxDownloadTaskStatus.Done, DownloadItemStatus.Completed)]
[TestCase(FreeboxDownloadTaskStatus.Unknown, DownloadItemStatus.Downloading)]
public void GetItems_should_return_item_as_downloadItemStatus(FreeboxDownloadTaskStatus apiStatus, DownloadItemStatus expectedItemStatus)
{
_task.Status = apiStatus;
GivenTasks(new List<FreeboxDownloadTask>() { _task });
var items = Subject.GetItems();
items.Should().HaveCount(1);
items.First().Status.Should().Be(expectedItemStatus);
}
[Test]
public void GetItems_should_return_decoded_destination_directory()
{
var decodedDownloadDirectory = "/that/the/path";
_task.Status = FreeboxDownloadTaskStatus.Done;
_task.DownloadDirectory = "L3RoYXQvdGhlL3BhdGg=";
GivenTasks(new List<FreeboxDownloadTask> { _task });
var items = Subject.GetItems();
items.Should().HaveCount(1);
items.First().OutputPath.Should().Be(decodedDownloadDirectory);
}
[Test]
public void GetItems_should_return_message_if_tasks_in_error()
{
_task.Status = FreeboxDownloadTaskStatus.Error;
_task.Error = "internal";
GivenTasks(new List<FreeboxDownloadTask> { _task });
var items = Subject.GetItems();
items.Should().HaveCount(1);
items.First().Message.Should().Be("Internal error.");
items.First().Status.Should().Be(DownloadItemStatus.Warning);
}
}
}

View File

@@ -27,7 +27,7 @@ namespace NzbDrone.Core.Test.Download
_downloadClients = new List<IDownloadClient>();
Mocker.GetMock<IProvideDownloadClient>()
.Setup(v => v.GetDownloadClients())
.Setup(v => v.GetDownloadClients(It.IsAny<bool>()))
.Returns(_downloadClients);
Mocker.GetMock<IProvideDownloadClient>()

View File

@@ -89,6 +89,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
.With(h => h.Title = title)
.With(h => h.Release = release)
.With(h => h.Reason = reason)
.With(h => h.ParsedMovieInfo = _parsedMovieInfo)
.Build();
_heldReleases.AddRange(heldReleases);

View File

@@ -52,6 +52,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
_pending.Add(new PendingRelease
{
Id = id,
Title = "Movie.Title.2020.720p-Radarr",
ParsedMovieInfo = new ParsedMovieInfo { MovieTitles = new List<string> { title }, Year = year },
MovieId = _movie.Id
});

View File

@@ -93,6 +93,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
.With(h => h.MovieId = _movie.Id)
.With(h => h.Title = title)
.With(h => h.Release = release)
.With(h => h.ParsedMovieInfo = _parsedMovieInfo)
.Build();
Mocker.GetMock<IPendingReleaseRepository>()

File diff suppressed because it is too large Load Diff

View File

@@ -24,7 +24,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
public void should_return_warning_when_download_client_has_not_been_configured()
{
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients())
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Returns(Array.Empty<IDownloadClient>());
Subject.Check().ShouldBeWarning();
@@ -40,7 +40,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Throws<Exception>();
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients())
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Returns(new IDownloadClient[] { downloadClient.Object });
Subject.Check().ShouldBeError();
@@ -55,7 +55,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(new List<DownloadClientItem>());
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients())
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Returns(new IDownloadClient[] { downloadClient.Object });
Subject.Check().ShouldBeOk();

View File

@@ -49,7 +49,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(_clientStatus);
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients())
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Returns(new IDownloadClient[] { _downloadClient.Object });
Mocker.GetMock<IDiskProvider>()

View File

@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Localization;
using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.HealthCheck.Checks
{
[TestFixture]
public class DownloadClientFolderCheckFixture : CoreTest<DownloadClientSortingCheck>
{
private DownloadClientInfo _clientStatus;
private Mock<IDownloadClient> _downloadClient;
private static Exception[] DownloadClientExceptions =
{
new DownloadClientUnavailableException("error"),
new DownloadClientAuthenticationException("error"),
new DownloadClientException("error")
};
[SetUp]
public void Setup()
{
Mocker.GetMock<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("Some Warning Message");
_clientStatus = new DownloadClientInfo
{
IsLocalhost = true,
SortingMode = null
};
_downloadClient = Mocker.GetMock<IDownloadClient>();
_downloadClient.Setup(s => s.Definition)
.Returns(new DownloadClientDefinition { Name = "Test" });
_downloadClient.Setup(s => s.GetStatus())
.Returns(_clientStatus);
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Returns(new IDownloadClient[] { _downloadClient.Object });
}
[Test]
public void should_return_ok_if_sorting_is_not_enabled()
{
Subject.Check().ShouldBeOk();
}
[Test]
public void should_return_warning_if_sorting_is_enabled()
{
_clientStatus.SortingMode = "TV";
Subject.Check().ShouldBeWarning();
}
[Test]
[TestCaseSource("DownloadClientExceptions")]
public void should_return_ok_if_client_throws_downloadclientexception(Exception ex)
{
_downloadClient.Setup(s => s.GetStatus())
.Throws(ex);
Subject.Check().ShouldBeOk();
ExceptionVerification.ExpectedErrors(0);
}
}
}

View File

@@ -63,7 +63,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(_clientStatus);
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients())
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Returns(new IDownloadClient[] { _downloadClient.Object });
Mocker.GetMock<IConfigService>()

View File

@@ -0,0 +1,133 @@
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.CustomFormats;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Housekeeping.Housekeepers;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
{
[TestFixture]
public class CleanupQualityProfileFormatItemsFixture : DbTest<CleanupQualityProfileFormatItems, Profile>
{
[SetUp]
public void Setup()
{
Mocker.SetConstant<IQualityProfileFormatItemsCleanupRepository>(
new QualityProfileFormatItemsCleanupRepository(Mocker.Resolve<IMainDatabase>(), Mocker.Resolve<IEventAggregator>()));
Mocker.SetConstant<ICustomFormatRepository>(
new CustomFormatRepository(Mocker.Resolve<IMainDatabase>(), Mocker.Resolve<IEventAggregator>()));
}
[Test]
public void should_remove_orphaned_custom_formats()
{
var qualityProfile = Builder<Profile>.CreateNew()
.With(h => h.Items = Qualities.QualityFixture.GetDefaultQualities())
.With(h => h.MinFormatScore = 50)
.With(h => h.CutoffFormatScore = 100)
.With(h => h.FormatItems = new List<ProfileFormatItem>
{
Builder<ProfileFormatItem>.CreateNew()
.With(c => c.Format = new CustomFormat("My Custom Format") { Id = 0 })
.Build()
})
.BuildNew();
Db.Insert(qualityProfile);
Subject.Clean();
var result = AllStoredModels;
result.Should().HaveCount(1);
result.First().FormatItems.Should().BeEmpty();
result.First().MinFormatScore.Should().Be(0);
result.First().CutoffFormatScore.Should().Be(0);
}
[Test]
public void should_not_remove_unorphaned_custom_formats()
{
var minFormatScore = 50;
var cutoffFormatScore = 100;
var customFormat = Builder<CustomFormat>.CreateNew()
.With(h => h.Specifications = new List<ICustomFormatSpecification>())
.BuildNew();
Db.Insert(customFormat);
var qualityProfile = Builder<Profile>.CreateNew()
.With(h => h.Items = Qualities.QualityFixture.GetDefaultQualities())
.With(h => h.MinFormatScore = minFormatScore)
.With(h => h.CutoffFormatScore = cutoffFormatScore)
.With(h => h.FormatItems = new List<ProfileFormatItem>
{
Builder<ProfileFormatItem>.CreateNew()
.With(c => c.Format = customFormat)
.Build()
})
.BuildNew();
Db.Insert(qualityProfile);
Subject.Clean();
var result = AllStoredModels;
result.Should().HaveCount(1);
result.First().FormatItems.Should().HaveCount(1);
result.First().MinFormatScore.Should().Be(minFormatScore);
result.First().CutoffFormatScore.Should().Be(cutoffFormatScore);
}
[Test]
public void should_add_missing_custom_formats()
{
var minFormatScore = 50;
var cutoffFormatScore = 100;
var customFormat1 = Builder<CustomFormat>.CreateNew()
.With(h => h.Id = 1)
.With(h => h.Name = "Custom Format 1")
.With(h => h.Specifications = new List<ICustomFormatSpecification>())
.BuildNew();
var customFormat2 = Builder<CustomFormat>.CreateNew()
.With(h => h.Id = 2)
.With(h => h.Name = "Custom Format 2")
.With(h => h.Specifications = new List<ICustomFormatSpecification>())
.BuildNew();
Db.Insert(customFormat1);
Db.Insert(customFormat2);
var qualityProfile = Builder<Profile>.CreateNew()
.With(h => h.Items = Qualities.QualityFixture.GetDefaultQualities())
.With(h => h.MinFormatScore = minFormatScore)
.With(h => h.CutoffFormatScore = cutoffFormatScore)
.With(h => h.FormatItems = new List<ProfileFormatItem>
{
Builder<ProfileFormatItem>.CreateNew()
.With(c => c.Format = customFormat1)
.Build()
})
.BuildNew();
Db.Insert(qualityProfile);
Subject.Clean();
var result = AllStoredModels;
result.Should().HaveCount(1);
result.First().FormatItems.Should().HaveCount(2);
result.First().MinFormatScore.Should().Be(minFormatScore);
result.First().CutoffFormatScore.Should().Be(cutoffFormatScore);
}
}
}

View File

@@ -1,10 +1,10 @@
using System.Collections.Generic;
using System.Collections.Generic;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.FileList;
using NzbDrone.Core.Indexers.Newznab;
using NzbDrone.Core.Indexers.Omgwtfnzbs;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Test.Framework;
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.Test.IndexerTests
_indexers = new List<IIndexer>();
_indexers.Add(Mocker.Resolve<Newznab>());
_indexers.Add(Mocker.Resolve<Omgwtfnzbs>());
_indexers.Add(Mocker.Resolve<FileList>());
Mocker.SetConstant<IEnumerable<IIndexer>>(_indexers);
}

View File

@@ -1,56 +0,0 @@
using System;
using System.Linq;
using System.Net.Http;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Omgwtfnzbs;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.IndexerTests.OmgwtfnzbsTests
{
[TestFixture]
public class OmgwtfnzbsFixture : CoreTest<Omgwtfnzbs>
{
[SetUp]
public void Setup()
{
Subject.Definition = new IndexerDefinition()
{
Name = "Omgwtfnzbs",
Settings = new OmgwtfnzbsSettings()
{
ApiKey = "xxx",
Username = "me@my.domain"
}
};
}
[Test]
public void should_parse_recent_feed_from_omgwtfnzbs()
{
var recentFeed = ReadAllText(@"Files/Indexers/Omgwtfnzbs/Omgwtfnzbs.xml");
Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
var releases = Subject.FetchRecent();
releases.Should().HaveCount(100);
var releaseInfo = releases.First();
releaseInfo.Title.Should().Be("Un.Petit.Boulot.2016.FRENCH.720p.BluRay.DTS.x264-LOST");
releaseInfo.DownloadProtocol.Should().Be(DownloadProtocol.Usenet);
releaseInfo.DownloadUrl.Should().Be("https://api.omgwtfnzbs.me/nzb/?id=8a2Bw&user=nzbdrone&api=nzbdrone");
releaseInfo.InfoUrl.Should().Be("https://omgwtfnzbs.me/details.php?id=8a2Bw");
releaseInfo.CommentUrl.Should().BeNullOrEmpty();
releaseInfo.Indexer.Should().Be(Subject.Definition.Name);
releaseInfo.PublishDate.Should().Be(DateTime.Parse("2017/01/09 00:16:54"));
releaseInfo.Size.Should().Be(5354909355);
}
}
}

View File

@@ -445,6 +445,58 @@ namespace NzbDrone.Core.Test.MediaFiles
.Verify(v => v.DeleteFolder(It.IsAny<string>(), true), Times.Never());
}
[Test]
public void should_return_rejection_if_nothing_imported_and_contains_rar_file()
{
GivenValidMovie();
var path = @"C:\media\ba09030e-1234-1234-1234-123456789abc\[HorribleSubs] American Psycho (2000) [720p]\[HorribleSubs] American Psycho (2000) [720p].mkv".AsOsAgnostic();
var imported = new List<ImportDecision>();
Mocker.GetMock<IMakeImportDecision>()
.Setup(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Movie>(), It.IsAny<DownloadClientItem>(), null, true, true))
.Returns(imported);
Mocker.GetMock<IImportApprovedMovie>()
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), true, null, ImportMode.Auto))
.Returns(imported.Select(i => new ImportResult(i)).ToList());
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetFiles(It.IsAny<string>(), SearchOption.AllDirectories))
.Returns(new[] { _videoFiles.First().Replace(".ext", ".rar") });
var result = Subject.ProcessPath(path);
result.Count.Should().Be(1);
result.First().Result.Should().Be(ImportResultType.Rejected);
}
[Test]
public void should_return_rejection_if_nothing_imported_and_contains_executable_file()
{
GivenValidMovie();
var path = @"C:\media\ba09030e-1234-1234-1234-123456789abc\[HorribleSubs] American Psycho (2000) [720p]\[HorribleSubs] American Psycho (2000) [720p].mkv".AsOsAgnostic();
var imported = new List<ImportDecision>();
Mocker.GetMock<IMakeImportDecision>()
.Setup(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Movie>(), It.IsAny<DownloadClientItem>(), null, true, true))
.Returns(imported);
Mocker.GetMock<IImportApprovedMovie>()
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), true, null, ImportMode.Auto))
.Returns(imported.Select(i => new ImportResult(i)).ToList());
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetFiles(It.IsAny<string>(), SearchOption.AllDirectories))
.Returns(new[] { _videoFiles.First().Replace(".ext", ".exe") });
var result = Subject.ProcessPath(path);
result.Count.Should().Be(1);
result.First().Result.Should().Be(ImportResultType.Rejected);
}
private void VerifyNoImport()
{
Mocker.GetMock<IImportApprovedMovie>().Verify(c => c.Import(It.IsAny<List<ImportDecision>>(), true, null, ImportMode.Auto),

View File

@@ -1,3 +1,4 @@
using System;
using System.Linq;
using Moq;
using NUnit.Framework;
@@ -13,10 +14,10 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.NotificationTests
{
[TestFixture]
public class TraktServiceFixture : CoreTest<TraktService>
public class TraktServiceFixture : CoreTest<Trakt>
{
private DownloadMessage _downloadMessage;
private TraktSettings _traktSettings;
private NotificationDefinition _traktDefinition;
[SetUp]
public void Setup()
@@ -34,11 +35,17 @@ namespace NzbDrone.Core.Test.NotificationTests
}
};
_traktSettings = new TraktSettings
_traktDefinition = new NotificationDefinition
{
AccessToken = "",
RefreshToken = ""
Settings = new TraktSettings
{
AccessToken = "",
RefreshToken = "",
Expires = DateTime.Now.AddDays(1)
}
};
Subject.Definition = _traktDefinition;
}
private void GiventValidMediaInfo(Quality quality, string audioChannels, string audioFormat, string scanType)
@@ -56,7 +63,7 @@ namespace NzbDrone.Core.Test.NotificationTests
[Test]
public void should_add_collection_movie_if_null_mediainfo()
{
Subject.AddMovieToCollection(_traktSettings, _downloadMessage.Movie, _downloadMessage.MovieFile);
Subject.OnDownload(_downloadMessage);
Mocker.GetMock<ITraktProxy>()
.Verify(v => v.AddToCollection(It.IsAny<TraktCollectMoviesResource>(), It.IsAny<string>()), Times.Once());
@@ -67,7 +74,7 @@ namespace NzbDrone.Core.Test.NotificationTests
{
GiventValidMediaInfo(Quality.Bluray1080p, "5.1", "DTS", "Progressive");
Subject.AddMovieToCollection(_traktSettings, _downloadMessage.Movie, _downloadMessage.MovieFile);
Subject.OnDownload(_downloadMessage);
Mocker.GetMock<ITraktProxy>()
.Verify(v => v.AddToCollection(It.Is<TraktCollectMoviesResource>(t =>
@@ -83,7 +90,7 @@ namespace NzbDrone.Core.Test.NotificationTests
{
GiventValidMediaInfo(Quality.Bluray1080p, "2.0", "DTS", "Progressive");
Subject.AddMovieToCollection(_traktSettings, _downloadMessage.Movie, _downloadMessage.MovieFile);
Subject.OnDownload(_downloadMessage);
Mocker.GetMock<ITraktProxy>()
.Verify(v => v.AddToCollection(It.Is<TraktCollectMoviesResource>(t =>

View File

@@ -49,6 +49,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Movie.Title.1990.Ultimate.Rekall.Edition.NORDiC.REMUX.1080p.BluRay.AVC.DTS-HD.MA5.1-TWA", "Ultimate Rekall Edition")]
[TestCase("Movie.Title.1971.Signature.Edition.1080p.BluRay.FLAC.2.0.x264-TDD", "Signature Edition")]
[TestCase("Movie.1979.The.Imperial.Edition.BluRay.720p.DTS.x264-CtrlHD", "Imperial Edition")]
[TestCase("Movie.1997.Open.Matte.1080p.BluRay.x264.DTS-FGT", "Open Matte")]
public void should_parse_edition(string postTitle, string edition)
{
var parsed = Parser.Parser.ParseMovieTitle(postTitle);

View File

@@ -44,6 +44,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Movie Name 2018 NEW PROPER 720p HD-CAM X264 HQ-CPG", true)]
[TestCase("Movie Name (2022) 1080p HQCAM ENG x264 AAC - QRips", false)]
[TestCase("Movie Name (2018) 720p Hindi HQ CAMrip x264 AAC 1.4GB", false)]
[TestCase("Movie Name (2022) New HDCAMRip 1080p [Love Rulz]", false)]
public void should_parse_cam(string title, bool proper)
{
ParseAndVerifyQuality(title, Source.CAM, proper, Resolution.Unknown);

View File

@@ -51,6 +51,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Some.Really.Bad.Movie.Title.[2021].1080p.WEB-HDRip.Dual.Audio.[Hindi.[Clean]. .English].x264.AAC.DD.2.0.By.Full4Movies.mkv-xpost", null)]
[TestCase("The.Movie.Title.2013.1080p.10bit.AMZN.WEB-DL.DDP5.1.HEVC-Vyndros", "Vyndros")]
[TestCase("Movie.Name.2022.1080p.BluRay.x264-[YTS.AG]", "YTS.AG")]
[TestCase("Movie.Name.2022.1080p.BluRay.x264-VARYG", "VARYG")]
[TestCase("Movie.Title.2019.1080p.AMZN.WEB-Rip.DDP.5.1.HEVC", null)]
public void should_parse_expected_release_group(string title, string expected)
{
@@ -107,6 +108,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Why.Cant.You.Use.Normal.Characters.2021.2160p.UHD.HDR10+.BluRay.TrueHD.Atmos.7.1.x265-ZØNEHD", "ZØNEHD")]
[TestCase("Movie.Should.Not.Use.Dots.2022.1080p.BluRay.x265.10bit.Tigole", "Tigole")]
[TestCase("Movie.Title.2005.2160p.UHD.BluRay.TrueHD 7.1.Atmos.x265 - HQMUX", "HQMUX")]
[TestCase("Movie.Name.2022.1080p.BluRay.x264-VARYG (Blue Lock, Multi-Subs)", "VARYG")]
public void should_parse_exception_release_group(string title, string expected)
{
Parser.Parser.ParseReleaseGroup(title).Should().Be(expected);

View File

@@ -24,7 +24,6 @@ namespace NzbDrone.Core.Test.UpdateTests
Subject.GetLatestUpdate("develop", new Version(10, 0)).Should().BeNull();
}
[Ignore("Pending linux-x86 release")]
[Test]
public void finds_update_when_version_lower()
{
@@ -40,7 +39,6 @@ namespace NzbDrone.Core.Test.UpdateTests
Subject.GetLatestUpdate("invalid_branch", new Version(0, 2)).Should().NotBeNull();
}
[Ignore("Pending linux-x86 release")]
[Test]
public void should_get_recent_updates()
{

View File

@@ -198,7 +198,7 @@ namespace NzbDrone.Core.Configuration
public string LogLevel => GetValue("LogLevel", "info").ToLowerInvariant();
public string ConsoleLogLevel => GetValue("ConsoleLogLevel", string.Empty, persist: false);
public string Theme => GetValue("Theme", "light", persist: false);
public string Theme => GetValue("Theme", "auto", persist: false);
public string PostgresHost => _postgresOptions?.Host ?? GetValue("PostgresHost", string.Empty, persist: false);
public string PostgresUser => _postgresOptions?.User ?? GetValue("PostgresUser", string.Empty, persist: false);
public string PostgresPassword => _postgresOptions?.Password ?? GetValue("PostgresPassword", string.Empty, persist: false);

View File

@@ -0,0 +1,14 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(217)]
public class remove_omg : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Delete.FromTable("Indexers").Row(new { Implementation = "Omgwtfnzbs" });
}
}
}

View File

@@ -144,7 +144,13 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
return true;
}
return false;
if ((isQualityUpgrade || isCustomFormatUpgrade) && !qualityProfile.UpgradeAllowed)
{
_logger.Debug("Quality profile does not allow upgrades, skipping");
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,27 @@
namespace NzbDrone.Core.Download.Clients.FreeboxDownload
{
public static class EncodingForBase64
{
public static string EncodeBase64(this string text)
{
if (text == null)
{
return null;
}
byte[] textAsBytes = System.Text.Encoding.UTF8.GetBytes(text);
return System.Convert.ToBase64String(textAsBytes);
}
public static string DecodeBase64(this string encodedText)
{
if (encodedText == null)
{
return null;
}
byte[] textAsBytes = System.Convert.FromBase64String(encodedText);
return System.Text.Encoding.UTF8.GetString(textAsBytes);
}
}
}

View File

@@ -0,0 +1,10 @@
namespace NzbDrone.Core.Download.Clients.FreeboxDownload
{
public class FreeboxDownloadException : DownloadClientException
{
public FreeboxDownloadException(string message)
: base(message)
{
}
}
}

View File

@@ -0,0 +1,8 @@
namespace NzbDrone.Core.Download.Clients.FreeboxDownload
{
public enum FreeboxDownloadPriority
{
Last = 0,
First = 1
}
}

View File

@@ -0,0 +1,277 @@
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Download.Clients.FreeboxDownload.Responses;
namespace NzbDrone.Core.Download.Clients.FreeboxDownload
{
public interface IFreeboxDownloadProxy
{
void Authenticate(FreeboxDownloadSettings settings);
string AddTaskFromUrl(string url, string directory, bool addPaused, bool addFirst, double? seedRatio, FreeboxDownloadSettings settings);
string AddTaskFromFile(string fileName, byte[] fileContent, string directory, bool addPaused, bool addFirst, double? seedRatio, FreeboxDownloadSettings settings);
void DeleteTask(string id, bool deleteData, FreeboxDownloadSettings settings);
FreeboxDownloadConfiguration GetDownloadConfiguration(FreeboxDownloadSettings settings);
List<FreeboxDownloadTask> GetTasks(FreeboxDownloadSettings settings);
}
public class FreeboxDownloadProxy : IFreeboxDownloadProxy
{
private readonly IHttpClient _httpClient;
private readonly Logger _logger;
private ICached<string> _authSessionTokenCache;
public FreeboxDownloadProxy(ICacheManager cacheManager, IHttpClient httpClient, Logger logger)
{
_httpClient = httpClient;
_logger = logger;
_authSessionTokenCache = cacheManager.GetCache<string>(GetType(), "authSessionToken");
}
public void Authenticate(FreeboxDownloadSettings settings)
{
var request = BuildRequest(settings).Resource("/login").Build();
var response = ProcessRequest<FreeboxLogin>(request, settings);
if (response.Result.LoggedIn == false)
{
throw new DownloadClientAuthenticationException("Not logged");
}
}
public string AddTaskFromUrl(string url, string directory, bool addPaused, bool addFirst, double? seedRatio, FreeboxDownloadSettings settings)
{
var request = BuildRequest(settings).Resource("/downloads/add").Post();
request.Headers.ContentType = "application/x-www-form-urlencoded";
request.AddFormParameter("download_url", System.Web.HttpUtility.UrlPathEncode(url));
if (!directory.IsNullOrWhiteSpace())
{
request.AddFormParameter("download_dir", directory);
}
var response = ProcessRequest<FreeboxDownloadTask>(request.Build(), settings);
SetTorrentSettings(response.Result.Id, addPaused, addFirst, seedRatio, settings);
return response.Result.Id;
}
public string AddTaskFromFile(string fileName, byte[] fileContent, string directory, bool addPaused, bool addFirst, double? seedRatio, FreeboxDownloadSettings settings)
{
var request = BuildRequest(settings).Resource("/downloads/add").Post();
request.AddFormUpload("download_file", fileName, fileContent, "multipart/form-data");
if (directory.IsNotNullOrWhiteSpace())
{
request.AddFormParameter("download_dir", directory);
}
var response = ProcessRequest<FreeboxDownloadTask>(request.Build(), settings);
SetTorrentSettings(response.Result.Id, addPaused, addFirst, seedRatio, settings);
return response.Result.Id;
}
public void DeleteTask(string id, bool deleteData, FreeboxDownloadSettings settings)
{
var uri = "/downloads/" + id;
if (deleteData == true)
{
uri += "/erase";
}
var request = BuildRequest(settings).Resource(uri).Build();
request.Method = HttpMethod.Delete;
ProcessRequest<string>(request, settings);
}
public FreeboxDownloadConfiguration GetDownloadConfiguration(FreeboxDownloadSettings settings)
{
var request = BuildRequest(settings).Resource("/downloads/config/").Build();
return ProcessRequest<FreeboxDownloadConfiguration>(request, settings).Result;
}
public List<FreeboxDownloadTask> GetTasks(FreeboxDownloadSettings settings)
{
var request = BuildRequest(settings).Resource("/downloads/").Build();
return ProcessRequest<List<FreeboxDownloadTask>>(request, settings).Result;
}
private static string BuildCachedHeaderKey(FreeboxDownloadSettings settings)
{
return $"{settings.Host}:{settings.AppId}:{settings.AppToken}";
}
private void SetTorrentSettings(string id, bool addPaused, bool addFirst, double? seedRatio, FreeboxDownloadSettings settings)
{
var request = BuildRequest(settings).Resource("/downloads/" + id).Build();
request.Method = HttpMethod.Put;
var body = new Dictionary<string, object> { };
if (addPaused)
{
body.Add("status", FreeboxDownloadTaskStatus.Stopped.ToString().ToLower());
}
if (addFirst)
{
body.Add("queue_pos", "1");
}
if (seedRatio != null)
{
// 0 means unlimited seeding
body.Add("stop_ratio", seedRatio);
}
if (body.Count == 0)
{
return;
}
request.SetContent(body.ToJson());
ProcessRequest<FreeboxDownloadTask>(request, settings);
}
private string GetSessionToken(HttpRequestBuilder requestBuilder, FreeboxDownloadSettings settings, bool force = false)
{
var sessionToken = _authSessionTokenCache.Find(BuildCachedHeaderKey(settings));
if (sessionToken == null || force)
{
_authSessionTokenCache.Remove(BuildCachedHeaderKey(settings));
_logger.Debug($"Client needs a new Session Token to reach the API with App ID '{settings.AppId}'");
// Obtaining a Session Token (from official documentation):
// To protect the app_token secret, it will never be used directly to authenticate the
// application, instead the API will provide a challenge the app will combine to its
// app_token to open a session and get a session_token.
// The validity of the session_token is limited in time and the app will have to renew
// this session_token once in a while.
// Retrieving the 'challenge' value (it changes frequently and have a limited time validity)
// needed to build password
var challengeRequest = requestBuilder.Resource("/login").Build();
challengeRequest.Method = HttpMethod.Get;
var challenge = ProcessRequest<FreeboxLogin>(challengeRequest, settings).Result.Challenge;
// The password is computed using the 'challenge' value and the 'app_token' ('App Token' setting)
var enc = System.Text.Encoding.ASCII;
var hmac = new HMACSHA1(enc.GetBytes(settings.AppToken));
hmac.Initialize();
var buffer = enc.GetBytes(challenge);
var password = System.BitConverter.ToString(hmac.ComputeHash(buffer)).Replace("-", "").ToLower();
// Both 'app_id' ('App ID' setting) and computed password are set to get a Session Token
var sessionRequest = requestBuilder.Resource("/login/session").Post().Build();
var body = new Dictionary<string, object>
{
{ "app_id", settings.AppId },
{ "password", password }
};
sessionRequest.SetContent(body.ToJson());
sessionToken = ProcessRequest<FreeboxLogin>(sessionRequest, settings).Result.SessionToken;
_authSessionTokenCache.Set(BuildCachedHeaderKey(settings), sessionToken);
_logger.Debug($"New Session Token stored in cache for App ID '{settings.AppId}', ready to reach API");
}
return sessionToken;
}
private HttpRequestBuilder BuildRequest(FreeboxDownloadSettings settings, bool authentication = true)
{
var requestBuilder = new HttpRequestBuilder(settings.UseSsl, settings.Host, settings.Port, settings.ApiUrl)
{
LogResponseContent = true
};
requestBuilder.Headers.ContentType = "application/json";
if (authentication == true)
{
requestBuilder.SetHeader("X-Fbx-App-Auth", GetSessionToken(requestBuilder, settings));
}
return requestBuilder;
}
private FreeboxResponse<T> ProcessRequest<T>(HttpRequest request, FreeboxDownloadSettings settings)
{
request.LogResponseContent = true;
request.SuppressHttpError = true;
HttpResponse response;
try
{
response = _httpClient.Execute(request);
}
catch (HttpRequestException ex)
{
throw new DownloadClientUnavailableException($"Unable to reach Freebox API. Verify 'Host', 'Port' or 'Use SSL' settings. (Error: {ex.Message})", ex);
}
catch (WebException ex)
{
throw new DownloadClientUnavailableException("Unable to connect to Freebox API, please check your settings", ex);
}
if (response.StatusCode == HttpStatusCode.Forbidden || response.StatusCode == HttpStatusCode.Unauthorized)
{
_authSessionTokenCache.Remove(BuildCachedHeaderKey(settings));
var responseContent = Json.Deserialize<FreeboxResponse<FreeboxLogin>>(response.Content);
var msg = $"Authentication to Freebox API failed. Reason: {responseContent.GetErrorDescription()}";
_logger.Error(msg);
throw new DownloadClientAuthenticationException(msg);
}
else if (response.StatusCode == HttpStatusCode.NotFound)
{
throw new FreeboxDownloadException("Unable to reach Freebox API. Verify 'API URL' setting for base URL and version.");
}
else if (response.StatusCode == HttpStatusCode.OK)
{
var responseContent = Json.Deserialize<FreeboxResponse<T>>(response.Content);
if (responseContent.Success)
{
return responseContent;
}
else
{
var msg = $"Freebox API returned error: {responseContent.GetErrorDescription()}";
_logger.Error(msg);
throw new DownloadClientException(msg);
}
}
else
{
throw new DownloadClientException("Unable to connect to Freebox, please check your settings.");
}
}
}
}

View File

@@ -0,0 +1,87 @@
using System.Text.RegularExpressions;
using FluentValidation;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
using NzbDrone.Core.Validation.Paths;
namespace NzbDrone.Core.Download.Clients.FreeboxDownload
{
public class FreeboxDownloadSettingsValidator : AbstractValidator<FreeboxDownloadSettings>
{
public FreeboxDownloadSettingsValidator()
{
RuleFor(c => c.Host).ValidHost();
RuleFor(c => c.Port).InclusiveBetween(1, 65535);
RuleFor(c => c.ApiUrl).NotEmpty()
.WithMessage("'API URL' must not be empty.");
RuleFor(c => c.ApiUrl).ValidUrlBase();
RuleFor(c => c.AppId).NotEmpty()
.WithMessage("'App ID' must not be empty.");
RuleFor(c => c.AppToken).NotEmpty()
.WithMessage("'App Token' must not be empty.");
RuleFor(c => c.Category).Matches(@"^\.?[-a-z]*$", RegexOptions.IgnoreCase)
.WithMessage("Allowed characters a-z and -");
RuleFor(c => c.DestinationDirectory).IsValidPath()
.When(c => c.DestinationDirectory.IsNotNullOrWhiteSpace());
RuleFor(c => c.DestinationDirectory).Empty()
.When(c => c.Category.IsNotNullOrWhiteSpace())
.WithMessage("Cannot use 'Category' and 'Destination Directory' at the same time.");
RuleFor(c => c.Category).Empty()
.When(c => c.DestinationDirectory.IsNotNullOrWhiteSpace())
.WithMessage("Cannot use 'Category' and 'Destination Directory' at the same time.");
}
}
public class FreeboxDownloadSettings : IProviderConfig
{
private static readonly FreeboxDownloadSettingsValidator Validator = new FreeboxDownloadSettingsValidator();
public FreeboxDownloadSettings()
{
Host = "mafreebox.freebox.fr";
Port = 443;
UseSsl = true;
ApiUrl = "/api/v1/";
}
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox, HelpText = "Hostname or host IP address of the Freebox, defaults to 'mafreebox.freebox.fr' (will only work if on same network)")]
public string Host { get; set; }
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox, HelpText = "Port used to access Freebox interface, defaults to '443'")]
public int Port { get; set; }
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secured connection when connecting to Freebox API")]
public bool UseSsl { get; set; }
[FieldDefinition(3, Label = "API URL", Type = FieldType.Textbox, Advanced = true, HelpText = "Define Freebox API base URL with API version, eg http://[host]:[port]/[api_base_url]/[api_version]/, defaults to '/api/v1/'")]
public string ApiUrl { get; set; }
[FieldDefinition(4, Label = "App ID", Type = FieldType.Textbox, HelpText = "App ID given when creating access to Freebox API (ie 'app_id')")]
public string AppId { get; set; }
[FieldDefinition(5, Label = "App Token", Type = FieldType.Password, Privacy = PrivacyLevel.Password, HelpText = "App token retrieved when creating access to Freebox API (ie 'app_token')")]
public string AppToken { get; set; }
[FieldDefinition(6, Label = "Destination Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default Freebox download location")]
public string DestinationDirectory { get; set; }
[FieldDefinition(7, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated non-Sonarr downloads (will create a [category] subdirectory in the output directory)")]
public string Category { get; set; }
[FieldDefinition(8, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(FreeboxDownloadPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
public int RecentPriority { get; set; }
[FieldDefinition(9, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(FreeboxDownloadPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
public int OlderPriority { get; set; }
[FieldDefinition(10, Label = "Add Paused", Type = FieldType.Checkbox)]
public bool AddPaused { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));
}
}
}

View File

@@ -0,0 +1,21 @@
using Newtonsoft.Json;
namespace NzbDrone.Core.Download.Clients.FreeboxDownload.Responses
{
public class FreeboxDownloadConfiguration
{
[JsonProperty(PropertyName = "download_dir")]
public string DownloadDirectory { get; set; }
public string DecodedDownloadDirectory
{
get
{
return DownloadDirectory.DecodeBase64();
}
set
{
DownloadDirectory = value.EncodeBase64();
}
}
}
}

View File

@@ -0,0 +1,137 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using NzbDrone.Common.Serializer;
namespace NzbDrone.Core.Download.Clients.FreeboxDownload.Responses
{
public enum FreeboxDownloadTaskType
{
Bt,
Nzb,
Http,
Ftp
}
public enum FreeboxDownloadTaskStatus
{
Unknown,
Stopped,
Queued,
Starting,
Downloading,
Stopping,
Error,
Done,
Checking,
Repairing,
Extracting,
Seeding,
Retry
}
public enum FreeboxDownloadTaskIoPriority
{
Low,
Normal,
High
}
public class FreeboxDownloadTask
{
private static readonly Dictionary<string, string> Descriptions;
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "download_dir")]
public string DownloadDirectory { get; set; }
public string DecodedDownloadDirectory
{
get
{
return DownloadDirectory.DecodeBase64();
}
set
{
DownloadDirectory = value.EncodeBase64();
}
}
[JsonProperty(PropertyName = "info_hash")]
public string InfoHash { get; set; }
[JsonProperty(PropertyName = "queue_pos")]
public int QueuePosition { get; set; }
[JsonConverter(typeof(UnderscoreStringEnumConverter), FreeboxDownloadTaskStatus.Unknown)]
public FreeboxDownloadTaskStatus Status { get; set; }
[JsonProperty(PropertyName = "eta")]
public long Eta { get; set; }
[JsonProperty(PropertyName = "error")]
public string Error { get; set; }
[JsonProperty(PropertyName = "type")]
public string Type { get; set; }
[JsonProperty(PropertyName = "io_priority")]
public string IoPriority { get; set; }
[JsonProperty(PropertyName = "stop_ratio")]
public long StopRatio { get; set; }
[JsonProperty(PropertyName = "piece_length")]
public long PieceLength { get; set; }
[JsonProperty(PropertyName = "created_ts")]
public long CreatedTimestamp { get; set; }
[JsonProperty(PropertyName = "size")]
public long Size { get; set; }
[JsonProperty(PropertyName = "rx_pct")]
public long ReceivedPrct { get; set; }
[JsonProperty(PropertyName = "rx_bytes")]
public long ReceivedBytes { get; set; }
[JsonProperty(PropertyName = "rx_rate")]
public long ReceivedRate { get; set; }
[JsonProperty(PropertyName = "tx_pct")]
public long TransmittedPrct { get; set; }
[JsonProperty(PropertyName = "tx_bytes")]
public long TransmittedBytes { get; set; }
[JsonProperty(PropertyName = "tx_rate")]
public long TransmittedRate { get; set; }
static FreeboxDownloadTask()
{
Descriptions = new Dictionary<string, string>
{
{ "internal", "Internal error." },
{ "disk_full", "The disk is full." },
{ "unknown", "Unknown error." },
{ "parse_error", "Parse error." },
{ "unknown_host", "Unknown host." },
{ "timeout", "Timeout." },
{ "bad_authentication", "Invalid credentials." },
{ "connection_refused", "Remote host refused connection." },
{ "bt_tracker_error", "Unable to announce on tracker." },
{ "bt_missing_files", "Missing torrent files." },
{ "bt_file_error", "Error accessing torrent files." },
{ "missing_ctx_file", "Error accessing task context file." },
{ "nzb_no_group", "Cannot find the requested group on server." },
{ "nzb_not_found", "Article not fount on the server." },
{ "nzb_invalid_crc", "Invalid article CRC." },
{ "nzb_invalid_size", "Invalid article size." },
{ "nzb_invalid_filename", "Invalid filename." },
{ "nzb_open_failed", "Error opening." },
{ "nzb_write_failed", "Error writing." },
{ "nzb_missing_size", "Missing article size." },
{ "nzb_decode_error", "Article decoding error." },
{ "nzb_missing_segments", "Missing article segments." },
{ "nzb_error", "Other nzb error." },
{ "nzb_authentication_required", "Nzb server need authentication." }
};
}
public string GetErrorDescription()
{
if (Descriptions.ContainsKey(Error))
{
return Descriptions[Error];
}
return $"{Error} - Unknown error";
}
}
}

View File

@@ -0,0 +1,18 @@
using Newtonsoft.Json;
namespace NzbDrone.Core.Download.Clients.FreeboxDownload.Responses
{
public class FreeboxLogin
{
[JsonProperty(PropertyName = "logged_in")]
public bool LoggedIn { get; set; }
[JsonProperty(PropertyName = "challenge")]
public string Challenge { get; set; }
[JsonProperty(PropertyName = "password_salt")]
public string PasswordSalt { get; set; }
[JsonProperty(PropertyName = "password_set")]
public bool PasswordSet { get; set; }
[JsonProperty(PropertyName = "session_token")]
public string SessionToken { get; set; }
}
}

View File

@@ -0,0 +1,69 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace NzbDrone.Core.Download.Clients.FreeboxDownload.Responses
{
public class FreeboxResponse<T>
{
private static readonly Dictionary<string, string> Descriptions;
[JsonProperty(PropertyName = "success")]
public bool Success { get; set; }
[JsonProperty(PropertyName = "msg")]
public string Message { get; set; }
[JsonProperty(PropertyName = "error_code")]
public string ErrorCode { get; set; }
[JsonProperty(PropertyName = "result")]
public T Result { get; set; }
static FreeboxResponse()
{
Descriptions = new Dictionary<string, string>
{
// Common errors
{ "invalid_request", "Your request is invalid." },
{ "invalid_api_version", "Invalid API base url or unknown API version." },
{ "internal_error", "Internal error." },
// Login API errors
{ "auth_required", "Invalid session token, or no session token sent." },
{ "invalid_token", "The app token you are trying to use is invalid or has been revoked." },
{ "pending_token", "The app token you are trying to use has not been validated by user yet." },
{ "insufficient_rights", "Your app permissions does not allow accessing this API." },
{ "denied_from_external_ip", "You are trying to get an app_token from a remote IP." },
{ "ratelimited", "Too many auth error have been made from your IP." },
{ "new_apps_denied", "New application token request has been disabled." },
{ "apps_denied", "API access from apps has been disabled." },
// Download API errors
{ "task_not_found", "No task was found with the given id." },
{ "invalid_operation", "Attempt to perform an invalid operation." },
{ "invalid_file", "Error with the download file (invalid format ?)." },
{ "invalid_url", "URL is invalid." },
{ "not_implemented", "Method not implemented." },
{ "out_of_memory", "No more memory available to perform the requested action." },
{ "invalid_task_type", "The task type is invalid." },
{ "hibernating", "The downloader is hibernating." },
{ "need_bt_stopped_done", "This action is only valid for Bittorrent task in stopped or done state." },
{ "bt_tracker_not_found", "Attempt to access an invalid tracker object." },
{ "too_many_tasks", "Too many tasks." },
{ "invalid_address", "Invalid peer address." },
{ "port_conflict", "Port conflict when setting config." },
{ "invalid_priority", "Invalid priority." },
{ "ctx_file_error", "Failed to initialize task context file (need to check disk)." },
{ "exists", "Same task already exists." },
{ "port_outside_range", "Incoming port is not available for this customer." }
};
}
public string GetErrorDescription()
{
if (Descriptions.ContainsKey(ErrorCode))
{
return Descriptions[ErrorCode];
}
return $"{ErrorCode} - Unknown error";
}
}
}

View File

@@ -0,0 +1,234 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download.Clients.FreeboxDownload.Responses;
using NzbDrone.Core.Download.Clients.QBittorrent;
using NzbDrone.Core.MediaFiles.TorrentInfo;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
namespace NzbDrone.Core.Download.Clients.FreeboxDownload
{
public class TorrentFreeboxDownload : TorrentClientBase<FreeboxDownloadSettings>
{
private readonly IFreeboxDownloadProxy _proxy;
public TorrentFreeboxDownload(IFreeboxDownloadProxy proxy,
ITorrentFileInfoReader torrentFileInfoReader,
IHttpClient httpClient,
IConfigService configService,
INamingConfigService namingConfigService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
ICacheManager cacheManager,
Logger logger)
: base(torrentFileInfoReader, httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, logger)
{
_proxy = proxy;
}
public override string Name => "Freebox Download";
protected IEnumerable<FreeboxDownloadTask> GetTorrents()
{
return _proxy.GetTasks(Settings).Where(v => v.Type.ToLower() == FreeboxDownloadTaskType.Bt.ToString().ToLower());
}
public override IEnumerable<DownloadClientItem> GetItems()
{
var torrents = GetTorrents();
var queueItems = new List<DownloadClientItem>();
foreach (var torrent in torrents)
{
var outputPath = new OsPath(torrent.DecodedDownloadDirectory);
if (Settings.DestinationDirectory.IsNotNullOrWhiteSpace())
{
if (!new OsPath(Settings.DestinationDirectory).Contains(outputPath))
{
continue;
}
}
if (Settings.Category.IsNotNullOrWhiteSpace())
{
var directories = outputPath.FullPath.Split('\\', '/');
if (!directories.Contains(Settings.Category))
{
continue;
}
}
var item = new DownloadClientItem()
{
DownloadId = torrent.Id,
Category = Settings.Category,
Title = torrent.Name,
TotalSize = torrent.Size,
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this),
RemainingSize = (long)(torrent.Size * (double)(1 - ((double)torrent.ReceivedPrct / 10000))),
RemainingTime = torrent.Eta <= 0 ? null : TimeSpan.FromSeconds(torrent.Eta),
SeedRatio = torrent.StopRatio <= 0 ? 0 : torrent.StopRatio / 100,
OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, outputPath)
};
switch (torrent.Status)
{
case FreeboxDownloadTaskStatus.Stopped: // task is stopped, can be resumed by setting the status to downloading
case FreeboxDownloadTaskStatus.Stopping: // task is gracefully stopping
item.Status = DownloadItemStatus.Paused;
break;
case FreeboxDownloadTaskStatus.Queued: // task will start when a new download slot is available the queue position is stored in queue_pos attribute
item.Status = DownloadItemStatus.Queued;
break;
case FreeboxDownloadTaskStatus.Starting: // task is preparing to start download
case FreeboxDownloadTaskStatus.Downloading:
case FreeboxDownloadTaskStatus.Retry: // you can set a task status to retry to restart the download task.
case FreeboxDownloadTaskStatus.Checking: // checking data before lauching download.
item.Status = DownloadItemStatus.Downloading;
break;
case FreeboxDownloadTaskStatus.Error: // there was a problem with the download, you can get an error code in the error field
item.Status = DownloadItemStatus.Warning;
item.Message = torrent.GetErrorDescription();
break;
case FreeboxDownloadTaskStatus.Done: // the download is over. For bt you can resume seeding setting the status to seeding if the ratio is not reached yet
case FreeboxDownloadTaskStatus.Seeding: // download is over, the content is Change to being shared to other users. The task will automatically stop once the seed ratio has been reached
item.Status = DownloadItemStatus.Completed;
break;
case FreeboxDownloadTaskStatus.Unknown:
default: // new status in API? default to downloading
item.Message = "Unknown download state: " + torrent.Status;
_logger.Info(item.Message);
item.Status = DownloadItemStatus.Downloading;
break;
}
item.CanBeRemoved = item.CanMoveFiles = torrent.Status == FreeboxDownloadTaskStatus.Done;
queueItems.Add(item);
}
return queueItems;
}
protected override string AddFromMagnetLink(RemoteMovie remoteMovie, string hash, string magnetLink)
{
return _proxy.AddTaskFromUrl(magnetLink,
GetDownloadDirectory().EncodeBase64(),
ToBePaused(),
ToBeQueuedFirst(remoteMovie),
GetSeedRatio(remoteMovie),
Settings);
}
protected override string AddFromTorrentFile(RemoteMovie remoteMovie, string hash, string filename, byte[] fileContent)
{
return _proxy.AddTaskFromFile(filename,
fileContent,
GetDownloadDirectory().EncodeBase64(),
ToBePaused(),
ToBeQueuedFirst(remoteMovie),
GetSeedRatio(remoteMovie),
Settings);
}
public override void RemoveItem(DownloadClientItem item, bool deleteData)
{
_proxy.DeleteTask(item.DownloadId, deleteData, Settings);
}
public override DownloadClientInfo GetStatus()
{
var destDir = GetDownloadDirectory();
return new DownloadClientInfo
{
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "::1" || Settings.Host == "localhost",
OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(destDir)) }
};
}
protected override void Test(List<ValidationFailure> failures)
{
try
{
_proxy.Authenticate(Settings);
}
catch (DownloadClientUnavailableException ex)
{
failures.Add(new ValidationFailure("Host", ex.Message));
failures.Add(new ValidationFailure("Port", ex.Message));
}
catch (DownloadClientAuthenticationException ex)
{
failures.Add(new ValidationFailure("AppId", ex.Message));
failures.Add(new ValidationFailure("AppToken", ex.Message));
}
catch (FreeboxDownloadException ex)
{
failures.Add(new ValidationFailure("ApiUrl", ex.Message));
}
}
private string GetDownloadDirectory()
{
if (Settings.DestinationDirectory.IsNotNullOrWhiteSpace())
{
return Settings.DestinationDirectory.TrimEnd('/');
}
var destDir = _proxy.GetDownloadConfiguration(Settings).DecodedDownloadDirectory.TrimEnd('/');
if (Settings.Category.IsNotNullOrWhiteSpace())
{
destDir = $"{destDir}/{Settings.Category}";
}
return destDir;
}
private bool ToBePaused()
{
return Settings.AddPaused;
}
private bool ToBeQueuedFirst(RemoteMovie remoteMovie)
{
var isRecentMovie = remoteMovie.Movie.MovieMetadata.Value.IsRecentMovie;
if ((isRecentMovie && Settings.RecentPriority == (int)FreeboxDownloadPriority.First) ||
(!isRecentMovie && Settings.OlderPriority == (int)FreeboxDownloadPriority.First))
{
return true;
}
return false;
}
private double? GetSeedRatio(RemoteMovie remoteMovie)
{
if (remoteMovie.SeedConfiguration == null || remoteMovie.SeedConfiguration.Ratio == null)
{
return null;
}
return remoteMovie.SeedConfiguration.Ratio.Value * 100;
}
}
}

View File

@@ -45,6 +45,11 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
return true;
}
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
throw new DownloadClientException("Failed to connect to qBittorrent. Check your settings and qBittorrent configuration.", new HttpException(response));
}
if (response.HasHttpError)
{
throw new DownloadClientException("Failed to connect to qBittorrent, check your settings.", new HttpException(response));

View File

@@ -262,6 +262,19 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
if (category != null)
{
if (config.Misc.enable_tv_sorting && ContainsCategory(config.Misc.tv_categories, Settings.MovieCategory))
{
status.SortingMode = "TV";
}
else if (config.Misc.enable_movie_sorting && ContainsCategory(config.Misc.movie_categories, Settings.MovieCategory))
{
status.SortingMode = "Movie";
}
else if (config.Misc.enable_date_sorting && ContainsCategory(config.Misc.date_categories, Settings.MovieCategory))
{
status.SortingMode = "Date";
}
status.OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, category.FullPath) };
}

View File

@@ -134,14 +134,33 @@ namespace NzbDrone.Core.Download
trackedDownload.Warn("No files found are eligible for import in {0}", outputPath);
}
if (importResults.Count == 1)
{
var firstResult = importResults.First();
if (firstResult.Result == ImportResultType.Rejected && firstResult.ImportDecision.LocalMovie == null)
{
trackedDownload.Warn(new TrackedDownloadStatusMessage(firstResult.Errors.First(), new List<string>()));
return;
}
}
var statusMessages = new List<TrackedDownloadStatusMessage>
{
new TrackedDownloadStatusMessage("One or more movies expected in this release were not imported or missing", new List<string>())
};
if (importResults.Any(c => c.Result != ImportResultType.Imported))
{
var statusMessages = importResults
statusMessages.AddRange(importResults
.Where(v => v.Result != ImportResultType.Imported && v.ImportDecision.LocalMovie != null)
.Select(v => new TrackedDownloadStatusMessage(Path.GetFileName(v.ImportDecision.LocalMovie.Path), v.Errors))
.ToArray();
.Select(v => new TrackedDownloadStatusMessage(Path.GetFileName(v.ImportDecision.LocalMovie.Path), v.Errors)));
trackedDownload.Warn(statusMessages);
if (statusMessages.Any())
{
trackedDownload.Warn(statusMessages.ToArray());
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using NzbDrone.Common.Disk;
namespace NzbDrone.Core.Download
@@ -11,6 +11,7 @@ namespace NzbDrone.Core.Download
}
public bool IsLocalhost { get; set; }
public string SortingMode { get; set; }
public List<OsPath> OutputRootFolders { get; set; }
}
}

View File

@@ -10,7 +10,7 @@ namespace NzbDrone.Core.Download
public interface IProvideDownloadClient
{
IDownloadClient GetDownloadClient(DownloadProtocol downloadProtocol, int indexerId = 0);
IEnumerable<IDownloadClient> GetDownloadClients();
IEnumerable<IDownloadClient> GetDownloadClients(bool filterBlockedClients = false);
IDownloadClient Get(int id);
}
@@ -86,14 +86,39 @@ namespace NzbDrone.Core.Download
return provider;
}
public IEnumerable<IDownloadClient> GetDownloadClients()
public IEnumerable<IDownloadClient> GetDownloadClients(bool filterBlockedClients = false)
{
return _downloadClientFactory.GetAvailableProviders();
var enabledClients = _downloadClientFactory.GetAvailableProviders();
if (filterBlockedClients)
{
return FilterBlockedIndexers(enabledClients).ToList();
}
return enabledClients;
}
public IDownloadClient Get(int id)
{
return _downloadClientFactory.GetAvailableProviders().Single(d => d.Definition.Id == id);
}
private IEnumerable<IDownloadClient> FilterBlockedIndexers(IEnumerable<IDownloadClient> clients)
{
var blockedClients = _downloadClientStatusService.GetBlockedProviders().ToDictionary(v => v.ProviderId, v => v);
foreach (var client in clients)
{
DownloadClientStatus blockedClientStatus;
if (blockedClients.TryGetValue(client.Definition.Id, out blockedClientStatus))
{
_logger.Debug("Temporarily ignoring client {0} till {1} due to recent failures.", client.Definition.Name, blockedClientStatus.DisabledTill.Value.ToLocalTime());
continue;
}
yield return client;
}
}
}
}

View File

@@ -109,7 +109,7 @@ namespace NzbDrone.Core.Download
movieGrabbedEvent.DownloadId = downloadClientId;
}
_logger.ProgressInfo("Report sent to {0} from indexer {1}. {2}", downloadClient.Definition.Name, remoteMovie.Release.Indexer, downloadTitle);
_logger.ProgressInfo("Report for {0} ({1}) sent to {2} from indexer {3}. {4}", remoteMovie.Movie.Title, remoteMovie.Movie.Year, downloadClient.Definition.Name, remoteMovie.Release.Indexer, downloadTitle);
_eventAggregator.PublishEvent(movieGrabbedEvent);
}
}

View File

@@ -283,6 +283,12 @@ namespace NzbDrone.Core.Download.Pending
return null;
}
// Languages will be empty if added before upgrading to v4, reparsing the languages if they're empty will set it to Unknown or better.
if (release.ParsedMovieInfo.Languages.Empty())
{
release.ParsedMovieInfo.Languages = LanguageParser.ParseLanguages(release.Title);
}
release.RemoteMovie = new RemoteMovie
{
Movie = movie,

View File

@@ -343,9 +343,12 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
if (movieFile.MediaInfo.Subtitles != null && movieFile.MediaInfo.Subtitles.Count > 0)
{
var subtitle = new XElement("subtitle");
subtitle.Add(new XElement("language", movieFile.MediaInfo.Subtitles));
streamDetails.Add(subtitle);
foreach (var s in movieFile.MediaInfo.Subtitles)
{
var subtitle = new XElement("subtitle");
subtitle.Add(new XElement("language", s));
streamDetails.Add(subtitle);
}
}
fileInfo.Add(streamDetails);

View File

@@ -1,5 +1,6 @@
using System;
using System.Linq;
using System.Net.Http;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore.Events;
@@ -37,7 +38,9 @@ namespace NzbDrone.Core.HealthCheck.Checks
public override HealthCheck Check()
{
var clients = _downloadClientProvider.GetDownloadClients();
// Only check clients not in failure status, those get another message
var clients = _downloadClientProvider.GetDownloadClients(true);
var rootFolders = _rootFolderService.All();
foreach (var client in clients)
@@ -58,6 +61,10 @@ namespace NzbDrone.Core.HealthCheck.Checks
{
_logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name);
}
catch (HttpRequestException ex)
{
_logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name);
}
catch (Exception ex)
{
_logger.Error(ex, "Unknown error occured in DownloadClientRootFolderCheck HealthCheck");

View File

@@ -0,0 +1,62 @@
using System;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.Localization;
using NzbDrone.Core.RemotePathMappings;
using NzbDrone.Core.RootFolders;
using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.HealthCheck.Checks
{
[CheckOn(typeof(ProviderUpdatedEvent<IDownloadClient>))]
[CheckOn(typeof(ProviderDeletedEvent<IDownloadClient>))]
[CheckOn(typeof(ModelEvent<RootFolder>))]
[CheckOn(typeof(ModelEvent<RemotePathMapping>))]
public class DownloadClientSortingCheck : HealthCheckBase
{
private readonly IProvideDownloadClient _downloadClientProvider;
private readonly Logger _logger;
public DownloadClientSortingCheck(IProvideDownloadClient downloadClientProvider,
Logger logger,
ILocalizationService localizationService)
: base(localizationService)
{
_downloadClientProvider = downloadClientProvider;
_logger = logger;
}
public override HealthCheck Check()
{
var clients = _downloadClientProvider.GetDownloadClients();
foreach (var client in clients)
{
try
{
var clientName = client.Definition.Name;
var status = client.GetStatus();
if (status.SortingMode.IsNotNullOrWhiteSpace())
{
return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("DownloadClientSortingCheckMessage"), clientName, status.SortingMode), "#download-folder-and-library-folder-not-different-folders");
}
}
catch (DownloadClientException ex)
{
_logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name);
}
catch (Exception ex)
{
_logger.Error(ex, "Unknown error occurred in DownloadClientSortingCheck HealthCheck");
}
}
return new HealthCheck(GetType());
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Linq;
using System.Net.Http;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
@@ -53,7 +54,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType());
}
var clients = _downloadClientProvider.GetDownloadClients();
// Only check clients not in failure status, those get another message
var clients = _downloadClientProvider.GetDownloadClients(true);
foreach (var client in clients)
{
@@ -100,6 +102,10 @@ namespace NzbDrone.Core.HealthCheck.Checks
{
_logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name);
}
catch (HttpRequestException ex)
{
_logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name);
}
catch (Exception ex)
{
_logger.Error(ex, "Unknown error occured in RemotePathMapping HealthCheck");
@@ -139,7 +145,14 @@ namespace NzbDrone.Core.HealthCheck.Checks
// If the previous case did not match then the failure occured in DownloadedMovieImportService,
// while trying to locate the files reported by the download client
var client = _downloadClientProvider.GetDownloadClients().FirstOrDefault(x => x.Definition.Name == failureMessage.DownloadClientInfo.Name);
// Only check clients not in failure status, those get another message
var client = _downloadClientProvider.GetDownloadClients(true).FirstOrDefault(x => x.Definition.Name == failureMessage.DownloadClientInfo.Name);
if (client == null)
{
return new HealthCheck(GetType());
}
try
{
var status = client.GetStatus();
@@ -192,6 +205,10 @@ namespace NzbDrone.Core.HealthCheck.Checks
{
_logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name);
}
catch (HttpRequestException ex)
{
_logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name);
}
catch (Exception ex)
{
_logger.Error(ex, "Unknown error occured in RemotePathMapping HealthCheck");

View File

@@ -0,0 +1,91 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.CustomFormats;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Profiles;
namespace NzbDrone.Core.Housekeeping.Housekeepers
{
public class CleanupQualityProfileFormatItems : IHousekeepingTask
{
private readonly IQualityProfileFormatItemsCleanupRepository _repository;
private readonly ICustomFormatRepository _customFormatRepository;
public CleanupQualityProfileFormatItems(IQualityProfileFormatItemsCleanupRepository repository,
ICustomFormatRepository customFormatRepository)
{
_repository = repository;
_customFormatRepository = customFormatRepository;
}
public void Clean()
{
var customFormats = _customFormatRepository.All().ToDictionary(c => c.Id);
var profiles = _repository.All();
var updatedProfiles = new List<Profile>();
foreach (var profile in profiles)
{
var formatItems = new List<ProfileFormatItem>();
// Make sure the profile doesn't include formats that have been removed
profile.FormatItems.ForEach(p =>
{
if (p.Format != null && customFormats.ContainsKey(p.Format.Id))
{
formatItems.Add(p);
}
});
// Make sure the profile includes all available formats
foreach (var customFormat in customFormats)
{
if (formatItems.None(f => f.Format.Id == customFormat.Key))
{
formatItems.Insert(0, new ProfileFormatItem
{
Format = customFormat.Value,
Score = 0
});
}
}
var previousIds = profile.FormatItems.Select(i => i.Format.Id).ToList();
var ids = formatItems.Select(i => i.Format.Id).ToList();
// Update the profile if any formats were added or removed
if (ids.Except(previousIds).Any() || previousIds.Except(ids).Any())
{
profile.FormatItems = formatItems;
if (profile.FormatItems.Empty())
{
profile.MinFormatScore = 0;
profile.CutoffFormatScore = 0;
}
updatedProfiles.Add(profile);
}
}
if (updatedProfiles.Any())
{
_repository.SetFields(updatedProfiles, p => p.FormatItems, p => p.MinFormatScore, p => p.CutoffFormatScore);
}
}
}
public interface IQualityProfileFormatItemsCleanupRepository : IBasicRepository<Profile>
{
}
public class QualityProfileFormatItemsCleanupRepository : BasicRepository<Profile>, IQualityProfileFormatItemsCleanupRepository
{
public QualityProfileFormatItemsCleanupRepository(IMainDatabase database, IEventAggregator eventAggregator)
: base(database, eventAggregator)
{
}
}
}

View File

@@ -36,7 +36,7 @@ namespace NzbDrone.Core.ImportLists.Trakt.List
var listName = Parser.Parser.ToUrlSlug(Settings.Listname.Trim(), true, "-", "-");
link += $"users/{Settings.Username.Trim()}/lists/{listName}/items/movies?limit={Settings.Limit}";
var request = new ImportListRequest(_traktProxy.BuildTraktRequest(link, HttpMethod.Get, Settings.AccessToken));
var request = new ImportListRequest(_traktProxy.BuildRequest(link, HttpMethod.Get, Settings.AccessToken));
yield return request;
}

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.ImportLists.ImportListMovies;
using NzbDrone.Core.Notifications.Trakt.Resource;
@@ -31,11 +31,11 @@ namespace NzbDrone.Core.ImportLists.Trakt.Popular
if (_settings.TraktListType == (int)TraktPopularListType.Popular)
{
jsonResponse = JsonConvert.DeserializeObject<List<TraktMovieResource>>(_importResponse.Content);
jsonResponse = STJson.Deserialize<List<TraktMovieResource>>(_importResponse.Content);
}
else
{
jsonResponse = JsonConvert.DeserializeObject<List<TraktListResource>>(_importResponse.Content).SelectList(c => c.Movie);
jsonResponse = STJson.Deserialize<List<TraktListResource>>(_importResponse.Content).SelectList(c => c.Movie);
}
// no movies were return

View File

@@ -71,7 +71,7 @@ namespace NzbDrone.Core.ImportLists.Trakt.Popular
link += filtersAndLimit;
var request = new ImportListRequest(_traktProxy.BuildTraktRequest(link, HttpMethod.Get, Settings.AccessToken));
var request = new ImportListRequest(_traktProxy.BuildRequest(link, HttpMethod.Get, Settings.AccessToken));
yield return request;
}

View File

@@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Net;
using Newtonsoft.Json;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.ImportLists.Exceptions;
using NzbDrone.Core.ImportLists.ImportListMovies;
using NzbDrone.Core.Notifications.Trakt.Resource;
@@ -27,7 +27,7 @@ namespace NzbDrone.Core.ImportLists.Trakt
return movies;
}
var jsonResponse = JsonConvert.DeserializeObject<List<TraktListResource>>(_importResponse.Content);
var jsonResponse = STJson.Deserialize<List<TraktListResource>>(_importResponse.Content);
// no movies were return
if (jsonResponse == null)

View File

@@ -42,7 +42,7 @@ namespace NzbDrone.Core.ImportLists.Trakt.User
break;
}
var request = new ImportListRequest(_traktProxy.BuildTraktRequest(link, HttpMethod.Get, Settings.AccessToken));
var request = new ImportListRequest(_traktProxy.BuildRequest(link, HttpMethod.Get, Settings.AccessToken));
yield return request;
}

View File

@@ -48,7 +48,6 @@ namespace NzbDrone.Core.Indexers.Newznab
yield return GetDefinition("NZBFinder.ws", GetSettings("https://nzbfinder.ws", categories: new[] { 2030, 2040, 2045, 2050, 2060, 2070, 2080, 2090 }));
yield return GetDefinition("NZBgeek", GetSettings("https://api.nzbgeek.info"));
yield return GetDefinition("nzbplanet.net", GetSettings("https://api.nzbplanet.net"));
yield return GetDefinition("omgwtfnzbs", GetSettings("https://api.omgwtfnzbs.me", categories: new[] { 2000, 2020, 2030, 2040, 2045, 2050, 2070 }));
yield return GetDefinition("OZnzb.com", GetSettings("https://api.oznzb.com"));
yield return GetDefinition("SimplyNZBs", GetSettings("https://simplynzbs.com"));
yield return GetDefinition("Tabula Rasa", GetSettings("https://www.tabula-rasa.pw", apiPath: @"/api/v1/api"));

View File

@@ -1,29 +0,0 @@
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Parser;
namespace NzbDrone.Core.Indexers.Omgwtfnzbs
{
public class Omgwtfnzbs : HttpIndexerBase<OmgwtfnzbsSettings>
{
public override string Name => "omgwtfnzbs";
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
public Omgwtfnzbs(IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, IParsingService parsingService, Logger logger)
: base(httpClient, indexerStatusService, configService, parsingService, logger)
{
}
public override IIndexerRequestGenerator GetRequestGenerator()
{
return new OmgwtfnzbsRequestGenerator() { Settings = Settings };
}
public override IParseIndexerResponse GetParser()
{
return new OmgwtfnzbsRssParser();
}
}
}

View File

@@ -1,59 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.IndexerSearch.Definitions;
namespace NzbDrone.Core.Indexers.Omgwtfnzbs
{
public class OmgwtfnzbsRequestGenerator : IIndexerRequestGenerator
{
public string BaseUrl { get; set; }
public OmgwtfnzbsSettings Settings { get; set; }
public OmgwtfnzbsRequestGenerator()
{
BaseUrl = "https://rss.omgwtfnzbs.me/rss-download.php";
}
public virtual IndexerPageableRequestChain GetRecentRequests()
{
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetPagedRequests(null));
return pageableRequests;
}
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
foreach (var queryTitle in searchCriteria.CleanSceneTitles)
{
pageableRequests.Add(GetPagedRequests(string.Format("{0}",
queryTitle)));
}
return pageableRequests;
}
private IEnumerable<IndexerRequest> GetPagedRequests(string query)
{
var url = new StringBuilder();
url.AppendFormat("{0}?catid=15,16,17,18,31,35&user={1}&api={2}&eng=1&delay={3}", BaseUrl, Settings.Username, Settings.ApiKey, Settings.Delay);
if (query.IsNotNullOrWhiteSpace())
{
url = url.Replace("rss-download.php", "rss-search.php");
url.AppendFormat("&search={0}", query);
}
yield return new IndexerRequest(url.ToString(), HttpAccept.Rss);
}
public Func<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
}
}

View File

@@ -1,50 +0,0 @@
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Indexers.Exceptions;
namespace NzbDrone.Core.Indexers.Omgwtfnzbs
{
public class OmgwtfnzbsRssParser : RssParser
{
public OmgwtfnzbsRssParser()
{
UseEnclosureUrl = true;
UseEnclosureLength = true;
}
protected override bool PreProcess(IndexerResponse indexerResponse)
{
var xdoc = LoadXmlDocument(indexerResponse);
var notice = xdoc.Descendants("notice").FirstOrDefault();
if (notice == null)
{
return true;
}
if (!notice.Value.ContainsIgnoreCase("api"))
{
return true;
}
throw new ApiKeyException(notice.Value);
}
protected override string GetInfoUrl(XElement item)
{
// Todo: Me thinks I need to parse details to get this...
var match = Regex.Match(item.Description(),
@"(?:\<b\>View NZB\:\<\/b\>\s\<a\shref\=\"")(?<URL>.+?)(?:\"")",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
if (match.Success)
{
return match.Groups["URL"].Value;
}
return string.Empty;
}
}
}

View File

@@ -1,49 +0,0 @@
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Omgwtfnzbs
{
public class OmgwtfnzbsSettingsValidator : AbstractValidator<OmgwtfnzbsSettings>
{
public OmgwtfnzbsSettingsValidator()
{
RuleFor(c => c.Username).NotEmpty();
RuleFor(c => c.ApiKey).NotEmpty();
RuleFor(c => c.Delay).GreaterThanOrEqualTo(0);
}
}
public class OmgwtfnzbsSettings : IIndexerSettings
{
private static readonly OmgwtfnzbsSettingsValidator Validator = new OmgwtfnzbsSettingsValidator();
public OmgwtfnzbsSettings()
{
Delay = 30;
MultiLanguages = new List<int>();
}
// Unused since Omg has a hardcoded url.
public string BaseUrl { get; set; }
[FieldDefinition(0, Label = "Username", Privacy = PrivacyLevel.UserName)]
public string Username { get; set; }
[FieldDefinition(1, Label = "API Key", Privacy = PrivacyLevel.ApiKey)]
public string ApiKey { get; set; }
[FieldDefinition(2, Label = "Delay", HelpText = "Time in minutes to delay new nzbs before they appear on the RSS feed", Advanced = true)]
public int Delay { get; set; }
[FieldDefinition(3, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));
}
}
}

View File

@@ -1 +1,7 @@
{}
{
"About": "সম্পর্কিত",
"AcceptConfirmationModal": "নিশ্চিতকরণ মডেল গ্রহণ করুন",
"Actions": "ক্রিয়াকাণ্ড",
"Activity": "কার্যকলাপ",
"Add": "যোগ করুন"
}

View File

@@ -5,7 +5,7 @@
"KeyboardShortcuts": "Keyboard Genveje",
"Info": "Information",
"IndexerStatusCheckSingleClientMessage": "Indexere utilgængelige på grund af fejl: {0}",
"IndexerStatusCheckAllClientMessage": "Alle indexere er utilgængelige på grund af fejl",
"IndexerStatusCheckAllClientMessage": "Alle indeksører er utilgængelige på grund af fejl",
"IndexersSettingsSummary": "Indexer og ugivelses restriktioner",
"IndexerSearchCheckNoInteractiveMessage": "Ingen indexere er tilgængelige med Interaktiv Søg aktiveret, Radarr vil ikke give nogle interaktive søge resultater",
"IndexerSearchCheckNoAvailableIndexersMessage": "Alle søge-mulige indexere er midlertidigt utilgængelige på grund af nylige indexer fejl",
@@ -89,7 +89,7 @@
"AudioInfo": "Lyd Info",
"Apply": "Anvend",
"AppDataLocationHealthCheckMessage": "Opdatering vil ikke være muligt for at undgå at slette AppData under opdatering",
"Analytics": "Analyser",
"Analytics": "Analyse",
"AlternativeTitle": "Alternativ Titel",
"AllMoviesHiddenDueToFilter": "Alle film er gemt på grund af aktivt filter.",
"All": "Alt",
@@ -140,7 +140,7 @@
"IncludeUnknownMovieItemsHelpText": "Vis emner uden en film i køen. Dette kan omfatte fjernede film eller andet i Radarrs kategori",
"IndexerPriorityHelpText": "Indekseringsprioritet fra 1 (højest) til 50 (lavest). Standard: 25.",
"LogLevelTraceHelpTextWarning": "Sporlogning bør kun aktiveres midlertidigt",
"MappedDrivesRunningAsService": "Kortlagte netværksdrev er ikke tilgængelige, når de kører som en Windows-tjeneste. Se FAQ for mere information",
"MappedDrivesRunningAsService": "Tilsluttede netværksdrev er ikke tilgængelige, når programmet kører som en Windows-tjeneste. Se FAQ'en for mere information",
"MassMovieSearch": "Massefilmsøgning",
"MIA": "MIA",
"MonitoredOnly": "Kun overvåget",
@@ -170,7 +170,7 @@
"UnableToLoadHistory": "Kunne ikke indlæse historikken",
"UnableToLoadMediaManagementSettings": "Kan ikke indlæse indstillinger for mediestyring",
"UnableToUpdateRadarrDirectly": "Kan ikke opdatere Radarr direkte,",
"BindAddressHelpText": "Gyldig IP4-adresse eller '*' for alle grænseflader",
"BindAddressHelpText": "Gyldig IP4-adresse, 'localhost' eller '*' for alle grænseflader",
"CreateEmptyMovieFoldersHelpText": "Opret manglende filmmapper under diskscanning",
"CouldNotConnectSignalR": "Kunne ikke oprette forbindelse til SignalR, UI opdateres ikke",
"Restrictions": "Begrænsninger",
@@ -212,7 +212,7 @@
"ChangeHasNotBeenSavedYet": "Ændring er endnu ikke gemt",
"CheckDownloadClientForDetails": "tjek download klient for flere detaljer",
"CheckForFinishedDownloadsInterval": "Kontroller for færdige downloadsinterval",
"AddIndexer": "Tilføj indexer",
"AddIndexer": "Tilføj indeksør",
"ChmodFolder": "chmod mappe",
"ChmodFolderHelpText": "Oktal, anvendt under import / omdøbning til mediemapper og filer (uden udførelse af bits)",
"ChmodFolderHelpTextWarning": "Dette fungerer kun, hvis den bruger, der kører Radarr, er ejeren af filen. Det er bedre at sikre, at downloadklienten indstiller tilladelserne korrekt.",
@@ -275,8 +275,8 @@
"ImportFailedInterp": "Import mislykkedes: {0}",
"IllRestartLater": "Jeg genstarter senere",
"InvalidFormat": "Ugyldigt format",
"LastDuration": "lastDuration",
"LastExecution": "Sidste henrettelse",
"LastDuration": "Seneste varighed",
"LastExecution": "Seneste udførelse",
"ListSyncLevelHelpTextWarning": "Filmfiler slettes permanent, dette kan resultere i at slette dit bibliotek, hvis dine lister er tomme",
"ListTagsHelpText": "Elementerne på tags-listen tilføjes med",
"MinAvailability": "Min tilgængelighed",
@@ -309,10 +309,10 @@
"ImportIncludeQuality": "Sørg for, at dine filer inkluderer kvaliteten i deres filnavne. f.eks. {0}",
"ImportListSyncIntervalHelpText": "Hvor ofte synkroniserer Radarr med dine lister. Minimumsværdi på 6 timer",
"IncludeHealthWarningsHelpText": "Inkluder sundhedsadvarsler",
"Max": "Maks",
"Max": "Maks.",
"Medium": "Medium",
"MovieFilesTotaling": "Totale filmfiler",
"OnGrab": "On Grab",
"OnGrab": "ved hentning",
"OnHealthIssue": "Om sundhedsspørgsmål",
"OnImport": "Ved import",
"OnLatestVersion": "Den seneste version af Radarr er allerede installeret",
@@ -407,7 +407,7 @@
"MovieIsMonitored": "Film overvåges",
"MovieIsUnmonitored": "Filmen overvåges ikke",
"Movies": "Film",
"NetCore": ".NET Core",
"NetCore": ".NET",
"OnHealthIssueHelpText": "Om sundhedsspørgsmål",
"OnRenameHelpText": "Om omdøb",
"OnUpgradeHelpText": "Ved opgradering",
@@ -563,7 +563,7 @@
"DestinationPath": "Destinationssti",
"DestinationRelativePath": "Destinationsrelateret sti",
"DetailedProgressBar": "Detaljeret statuslinje",
"Discord": "Uenighed",
"Discord": "Discord",
"Docker": "Docker",
"Donations": "Donationer",
"DoneEditingGroups": "Udført redigering af grupper",
@@ -689,7 +689,7 @@
"ManualImportSelectMovie": "Manuel import - Vælg film",
"ManualImportSelectQuality": " Manuel import - Vælg kvalitet",
"MarkAsFailedMessageText": "Er du sikker på, at du vil markere '{0}' som mislykket?",
"MaximumLimits": "Maksimale grænser",
"MaximumLimits": "Maksimumgrænser",
"MaximumSize": "Maksimal størrelse",
"MegabytesPerMinute": "Megabyte pr. Minut",
"Message": "Besked",
@@ -951,7 +951,7 @@
"UnableToLoadDownloadClients": "Kunne ikke indlæse downloadklienter",
"UnableToLoadGeneralSettings": "Kan ikke indlæse generelle indstillinger",
"UnableToLoadIndexerOptions": "Kan ikke indlæse indekseringsindstillinger",
"UnableToLoadIndexers": "Kan ikke indlæse indeksatorer",
"UnableToLoadIndexers": "Kan ikke indlæse indeksørerne",
"UnableToLoadLanguages": "Sprog kunne ikke indlæses",
"UnableToLoadListExclusions": "Kunne ikke indlæse listeekskluderinger",
"UnableToLoadListOptions": "Kan ikke indlæse listeindstillinger",

View File

@@ -329,7 +329,7 @@
"BackupRetentionHelpText": "Automatische Backups, die älter als die Aufbewahrungsfrist sind, werden automatisch gelöscht",
"Backups": "Backups",
"BindAddress": "Adresse binden",
"BindAddressHelpText": "Gültige IPv4 Adresse oder \"*\" für alle Netzwerke",
"BindAddressHelpText": "Gültige IP Adresse oder \"*\" für alle Netzwerke",
"Branch": "Git-Branch",
"BypassProxyForLocalAddresses": "Proxy für lokale Adressen umgehen",
"CertificateValidation": "Zertifikat Validierung",
@@ -979,7 +979,7 @@
"EditCustomFormat": "Eigenes Format bearbeiten",
"DoNotUpgradeAutomatically": "Nicht automatisch upgraden",
"DoNotPrefer": "Nicht bevorzugen",
"DoneEditingGroups": "Gruppen bearbeiten abgechlossen",
"DoneEditingGroups": "Editieren der Gruppen abschließen",
"Donations": "Spenden",
"DockerUpdater": "aktualisiere den Docker Container um das Update zu erhalten",
"Discord": "Discord",
@@ -1150,8 +1150,10 @@
"AreYouSureYouWantToResetQualityDefinitions": "Sicher, dass die Qualitätsdefinitionen zurückgesetzt werden sollen?",
"ResetDefinitions": "Definitionen zurücksetzen",
"ResetQualityDefinitions": "Qualitätsdefinitionen zurücksetzen",
"SettingsThemeHelpText": "Anwendungsdesign ändern, inspiriert von Theme.Park",
"SettingsThemeHelpText": "Anwendungsdesign ändern, das 'Auto' Design passt sich an den Light/Dark-Mode deines Systems an. Inspiriert von Theme.Park",
"ResetTitles": "Titel zurücksetzen",
"ResetTitlesHelpText": "Definitionstitel und Werte zurücksetzen",
"SettingsTheme": "Design"
"SettingsTheme": "Design",
"RSSHelpText": "Wird benutzt, wenn Radarr mittels RSS-Sync regelmäßig nach Releases schaut",
"DownloadClientSortingCheckMessage": "Downloader {0} hat die {1} Sortierung für Radarrs Kategorie aktiviert. Dies sollte deaktiviert werden, um Import-Probleme zu vermeiden."
}

View File

@@ -57,11 +57,11 @@
"DownloadClientStatusCheckAllClientMessage": "Όλα τα προγράμματα λήψης είναι μη διαθέσιμα λόγων αποτυχιών",
"DownloadClientsSettingsSummary": "Προγράμματα λήψης, διαχείριση λήψεων και αντιστοίχηση remote path",
"DelayProfiles": "Προφίλ χρονοκαθυστέρησης",
"CustomFormatsSettingsSummary": "Custom Formats και Ρυθμίσεις",
"CustomFormats": "Custom Formats",
"CustomFormatsSettingsSummary": "Προσαρμοσμένες Μορφές και Ρυθμίσεις",
"CustomFormats": "Προσαρμοσμένες Μορφές",
"CustomFilters": "Custom Φιλτρα",
"Crew": "Ομάδα",
"ConnectSettingsSummary": "Ειδοποιήσεις, συνδέσεις σε media servers/players και custom scripts",
"ConnectSettingsSummary": "Ειδοποιήσεις, συνδέσεις με διακομιστές πολυμέσων/προγράμματα αναπαραγωγής και προσαρμοσμένα σενάρια",
"AppDataLocationHealthCheckMessage": "Η αναβάθμιση δεν είναι πιθανό να αποτρέψει την διαγραφή των AppData κατά την αναβάθμιση",
"AddNewTmdbIdMessage": "Μπορείτε επίσης να ψάξετε μέσω του TMDB Id της ταινίας. π.χ. tmdb:71663",
"AddNewMessage": "Είναι εύκολο να προσθέσετε μια καινούρια ταινία, απλά πληκτρολογήστε το όνομα της ταινίας",
@@ -73,7 +73,7 @@
"AudioInfo": "Στοιχεία ήχου",
"Apply": "Εφαρμογή",
"AlternativeTitle": "Εναλακτικός Τίτλος",
"AllMoviesHiddenDueToFilter": "Όλες οι ταινίες έχουν κρυφτεί λόγω εφαρμογής φίλτρου",
"AllMoviesHiddenDueToFilter": "Όλες οι ταινίες έχουν κρυφτεί λόγω εφαρμογής φίλτρου.",
"Age": "Ηλικία",
"AddNewMovie": "Προσθήκη Νέας Ταινίας",
"AddList": "Προσθήκη Λίστας",
@@ -189,7 +189,7 @@
"AutoUnmonitorPreviouslyDownloadedMoviesHelpText": "Οι ταινίες που διαγράφονται από το δίσκο δεν παρακολουθούνται αυτόματα στο Radarr",
"AvailabilityDelayHelpText": "Ποσό χρόνου πριν ή μετά τη διαθέσιμη ημερομηνία για αναζήτηση ταινίας",
"BindAddress": "Δεσμευμένη διεύθυνση",
"BindAddressHelpText": "Έγκυρη διεύθυνση IP4 ή «*» για όλες τις διεπαφές",
"BindAddressHelpText": "Έγκυρη διεύθυνση IP, localhost ή '*' για όλες τις διεπαφές",
"Branch": "Κλαδί",
"ChangeHasNotBeenSavedYet": "Η αλλαγή δεν έχει αποθηκευτεί ακόμα",
"CheckDownloadClientForDetails": "ελέγξτε το πρόγραμμα-πελάτη λήψης για περισσότερες λεπτομέρειες",
@@ -205,7 +205,7 @@
"CancelProcessing": "Ακύρωση επεξεργασίας",
"CantFindMovie": "Γιατί δεν μπορώ να βρω την ταινία μου;",
"CertificateValidation": "Επικύρωση πιστοποιητικού",
"CertificateValidationHelpText": "Αλλάξτε πόσο αυστηρή είναι η επικύρωση πιστοποίησης HTTPS",
"CertificateValidationHelpText": "Αλλάξτε πόσο αυστηρή είναι η επικύρωση πιστοποίησης HTTPS.",
"CleanLibraryLevel": "Καθαρό επίπεδο βιβλιοθήκης",
"ChmodFolderHelpTextWarning": "Αυτό λειτουργεί μόνο εάν ο χρήστης που εκτελεί το Radarr είναι ο κάτοχος του αρχείου. Είναι καλύτερο να διασφαλίσετε ότι ο πελάτης λήψης ορίζει σωστά τα δικαιώματα.",
"DeleteBackup": "Διαγραφή αντιγράφων ασφαλείας",
@@ -266,7 +266,7 @@
"ImportMechanismHealthCheckMessage": "Ενεργοποίηση ολοκληρωμένου χειρισμού λήψεων",
"IncludeHealthWarningsHelpText": "Συμπεριλάβετε προειδοποιήσεις για την υγεία",
"InvalidFormat": "Ακυρη μορφή",
"LastDuration": "τελευταία Διάρκεια",
"LastDuration": "Τελευταία Διάρκεια",
"LastExecution": "Τελευταία εκτέλεση",
"ListSyncLevelHelpTextWarning": "Τα αρχεία ταινιών θα διαγραφούν οριστικά. Αυτό μπορεί να οδηγήσει σε διαγραφή της βιβλιοθήκης σας εάν οι λίστες σας είναι κενές",
"Max": "Μέγιστη",
@@ -276,7 +276,7 @@
"MinimumAge": "Ελάχιστη ηλικία",
"MinimumFreeSpace": "Ελάχιστος ελεύθερος χώρος",
"Minutes": "Λεπτά",
"MissingFromDisk": "Ο Radarr δεν μπόρεσε να βρει το αρχείο στο δίσκο και έτσι καταργήθηκε",
"MissingFromDisk": "Το Radarr δεν μπόρεσε να βρει το αρχείο στο δίσκο και έτσι αφαιρέθηκε το αρχείο αποσυνδέθηκε από την ταινία στη βάση δεδομένων",
"Monday": "Δευτέρα",
"MoveFiles": "Μετακίνηση αρχείων",
"MovieFilesTotaling": "Συνολικά αρχεία ταινιών",
@@ -318,8 +318,8 @@
"PriorityHelpText": "Προτεραιότητα πολλαπλών πελατών λήψης. Το Round-Robin χρησιμοποιείται για πελάτες με την ίδια προτεραιότητα.",
"Profiles": "Προφίλ",
"ProxyType": "Τύπος διακομιστή μεσολάβησης",
"QualitiesHelpText": ροτιμώνται οι υψηλότερες ποιότητες στη λίστα. Οι ιδιότητες εντός της ίδιας ομάδας είναι ίδιες. Απαιτούνται μόνο ελεγμένες ιδιότητες",
"QualityProfileInUse": "Δεν είναι δυνατή η διαγραφή ενός ποιοτικού προφίλ που είναι συνημμένο σε μια ταινία",
"QualitiesHelpText": οιότητες υψηλότερες στη λίστα προτιμώνται περισσότερο, ακόμη και αν δεν έχουν επιλεγεί. Οι ιδιότητες στην ίδια ομάδα είναι ίσες. Ζητούνται μόνο ελεγμένες ιδιότητες",
"QualityProfileInUse": "Δεν είναι δυνατή η διαγραφή ενός προφίλ ποιότητας που είναι συνδεδεμένο σε ταινία, λίστα ή συλλογή",
"QualitySettings": "Ρυθμίσεις ποιότητας",
"QueueIsEmpty": "Η ουρά είναι κενή",
"RadarrCalendarFeed": "Ροή ημερολογίου Radarr",
@@ -414,7 +414,7 @@
"MustContain": "Πρέπει να περιέχει",
"MustNotContain": "Δεν πρέπει να περιέχει",
"NamingSettings": "Ρυθμίσεις ονομάτων",
"NetCore": ".NET Core",
"NetCore": ".NET",
"NoBackupsAreAvailable": "Δεν υπάρχουν διαθέσιμα αντίγραφα ασφαλείας",
"NoChange": "Καμία αλλαγή",
"NoHistory": "Χωρίς ιστορία",
@@ -457,8 +457,8 @@
"AllowHardcodedSubsHelpText": "Οι ανιχνευόμενοι κωδικοποιημένοι συνδρομητές θα ληφθούν αυτόματα",
"QualityProfile": "Ποιοτικό προφίλ",
"QualityProfiles": "Προφίλ ποιότητας",
"QuickImport": "Γρήγορη εισαγωγή",
"RadarrSupportsAnyDownloadClient": "Το Radarr υποστηρίζει οποιονδήποτε πελάτη λήψης που χρησιμοποιεί το πρότυπο Newznab, καθώς και άλλους πελάτες λήψης που αναφέρονται παρακάτω.",
"QuickImport": "Αυτόματη Μετακίνησή",
"RadarrSupportsAnyDownloadClient": "Το Radarr υποστηρίζει πολλούς δημοφιλείς πελάτες λήψης torrent και usenet.",
"AlreadyInYourLibrary": "Ήδη στη βιβλιοθήκη σας",
"RecycleBinCleanupDaysHelpTextWarning": "Τα αρχεία στον κάδο ανακύκλωσης παλαιότερα από τον επιλεγμένο αριθμό ημερών θα καθαρίζονται αυτόματα",
"RecycleBinHelpText": "Τα αρχεία ταινιών θα μεταβούν εδώ όταν διαγραφούν αντί να διαγραφούν οριστικά",
@@ -641,7 +641,7 @@
"IncludeUnmonitored": "Συμπερίληψη χωρίς παρακολούθηση",
"ImportMovies": "Εισαγωγή ταινιών",
"IndexerPriority": "Προτεραιότητα ευρετηρίου",
"IndexerPriorityHelpText": "Προτεραιότητα ευρετηρίου από 1 (Υψηλότερη) έως 50 (Χαμηλότερη). Προεπιλογή: 25.",
"IndexerPriorityHelpText": "Προτεραιότητα δείκτη από 1 (υψηλότερη) έως 50 (χαμηλότερη). Προεπιλογή: 25. Χρησιμοποιείται κατά την κατάκτηση εκδόσεων ως ισοπαλία για ίσες εκδόσεις, το Radarr θα εξακολουθεί να χρησιμοποιεί όλα τα ενεργοποιημένα ευρετήρια για RSS Sync και Search",
"IndexerRssHealthCheckNoAvailableIndexers": "Όλοι οι δείκτες με δυνατότητα rss δεν είναι διαθέσιμοι προσωρινά λόγω πρόσφατων σφαλμάτων ευρετηρίου",
"IndexerRssHealthCheckNoIndexers": "Δεν υπάρχουν διαθέσιμα ευρετήρια με ενεργοποιημένο τον συγχρονισμό RSS, το Radarr δεν θα τραβήξει αυτόματα νέες κυκλοφορίες",
"Indexers": "Ευρετήρια",
@@ -824,7 +824,7 @@
"SearchMissing": "Λείπει η αναζήτηση",
"SearchMovie": "Αναζήτηση ταινίας",
"SearchOnAdd": "Αναζήτηση στο Προσθήκη",
"SearchOnAddHelpText": "Αναζητήστε ταινίες σε αυτήν τη λίστα όταν προστεθείτε στο Radarr",
"SearchOnAddHelpText": "Αναζητήστε ταινίες σε αυτήν τη λίστα όταν προστεθούν στη βιβλιοθήκη",
"SearchSelected": "Επιλεγμένη αναζήτηση",
"Seconds": "Δευτερόλεπτα",
"Security": "Ασφάλεια",
@@ -854,7 +854,7 @@
"SettingsTimeFormat": "Μορφή ώρας",
"SettingsWeekColumnHeader": "Κεφαλίδα στήλης εβδομάδας",
"SettingsWeekColumnHeaderHelpText": "Εμφανίζεται πάνω από κάθε στήλη όταν η εβδομάδα είναι η ενεργή προβολή",
"ShouldMonitorHelpText": "Εάν ενεργοποιηθεί, προστίθενται και παρακολουθούνται ταινίες που προστίθενται από αυτήν τη λίστα",
"ShouldMonitorHelpText": "Εάν οι ταινίες ή οι συλλογές που προστίθενται από αυτήν τη λίστα πρέπει να προστεθούν υπό παρακολούθηση",
"ShowAdvanced": "Εμφάνιση για προχωρημένους",
"ShowAsAllDayEvents": "Εμφάνιση ως ολοήμερων εκδηλώσεων",
"ShowCutoffUnmetIconHelpText": "Εμφάνιση εικονιδίου για αρχεία όταν δεν έχει τηρηθεί το όριο",
@@ -912,7 +912,7 @@
"Tasks": "Καθήκοντα",
"Test": "Δοκιμή",
"TestAll": "Δοκιμάστε όλα",
"ThisCannotBeCancelled": "Αυτό δεν μπορεί να ακυρωθεί μόλις ξεκινήσει χωρίς επανεκκίνηση του Radarr.",
"ThisCannotBeCancelled": "Αυτό δεν μπορεί να ακυρωθεί μόλις ξεκινήσει η απενεργοποίηση όλων των ευρετηριωτών σας.",
"Time": "χρόνος",
"Title": "Τίτλος",
"Titles": "Τίτλοι",
@@ -989,7 +989,7 @@
"UpgradeUntilCustomFormatScore": "Αναβάθμιση Μέχρι Βαθμολογία προσαρμοσμένης μορφής",
"UpgradeUntilQuality": "Αναβάθμιση έως την ποιότητα",
"UpgradeUntilThisQualityIsMetOrExceeded": "Αναβαθμίστε έως ότου ικανοποιηθεί ή ξεπεραστεί αυτή η ποιότητα",
"UpperCase": "Άνω θήκη",
"UpperCase": "Κεφαλαία",
"URLBase": "Βάση διεύθυνσης URL",
"UrlBaseHelpText": "Για αντίστροφη υποστήριξη διακομιστή μεσολάβησης, η προεπιλογή είναι άδεια",
"UseHardlinksInsteadOfCopy": "Χρησιμοποιήστε Hardlinks αντί για Αντιγραφή",
@@ -1058,9 +1058,102 @@
"List": "Τόπος αγώνων",
"Rating": "Ακροαματικότητα",
"Filters": "Φίλτρο",
"AllCollectionsHiddenDueToFilter": "Όλες οι ταινίες έχουν κρυφτεί λόγω εφαρμογής φίλτρου",
"AllCollectionsHiddenDueToFilter": "Όλες οι ταινίες έχουν κρυφτεί λόγω εφαρμογής φίλτρου.",
"Collections": "Συλλογή",
"MonitorMovies": "Παρακολούθηση ταινίας",
"NoCollections": "Δεν βρέθηκαν ταινίες, για να ξεκινήσετε θα θέλετε να προσθέσετε μια νέα ταινία ή να εισαγάγετε ορισμένες υπάρχουσες.",
"RssSyncHelpText": "Διάστημα σε λεπτά. Ρυθμίστε στο μηδέν για απενεργοποίηση (αυτό θα σταματήσει όλες τις αυτόματες αρπάξεις απελευθέρωσης)"
"NoCollections": "Δεν βρέθηκαν ταινίες, για να ξεκινήσετε θα θέλετε να προσθέσετε μια νέα ταινία ή να εισαγάγετε ορισμένες υπάρχουσες",
"RssSyncHelpText": "Διάστημα σε λεπτά. Ρυθμίστε στο μηδέν για απενεργοποίηση (αυτό θα σταματήσει όλες τις αυτόματες αρπάξεις απελευθέρωσης)",
"CollectionsSelectedInterp": "Επιλέχθηκαν {0} συλλογές",
"MovieOnly": "Μόνο ταινία",
"ChooseImportMode": "Επιλέξτε Λειτουργία εισαγωγής",
"Duration": "Διάρκεια",
"ImdbVotes": "Ψήφοι στο IMDb",
"ImportListMultipleMissingRoots": "Λείπουν πολλοί ριζικοί φάκελοι για λίστες εισαγωγής: {0}",
"MonitorCollection": "Συλλογή οθονών",
"MonitoredCollectionHelpText": "Παρακολουθήστε για αυτόματη προσθήκη στη βιβλιοθήκη ταινιών από αυτήν τη συλλογή",
"MovieCollectionMultipleMissingRoots": "Λείπουν πολλοί ριζικοί φάκελοι για τις συλλογές ταινιών: {0}",
"IndexerJackettAll": "Ευρετήρια που χρησιμοποιούν το μη υποστηριζόμενο τελικό σημείο Jackett 'all': {0}",
"AnnouncedMsg": "Η ταινία έχει ανακοινωθεί",
"Letterboxd": "Γραμματοκιβώτιο",
"EditCollection": "Επεξεργασία συλλογής",
"ImdbRating": "Αξιολόγηση στο IMDb",
"InstanceNameHelpText": "Όνομα παρουσίας στην καρτέλα και για όνομα εφαρμογής Syslog",
"MovieCollectionMissingRoot": "Λείπει ο ριζικός φάκελος για τη συλλογή ταινιών: {0}",
"OnApplicationUpdate": "Στην ενημέρωση της εφαρμογής",
"OnApplicationUpdateHelpText": "Στην ενημέρωση της εφαρμογής",
"OnMovieAdded": "Προστέθηκε στην ταινία",
"OnMovieAddedHelpText": "Προστέθηκε στην ταινία",
"ApplicationUrlHelpText": "Το εξωτερικό URL αυτής της εφαρμογής, συμπεριλαμβανομένων των http(s)://, της θύρας και της βάσης URL",
"Auto": "Αυτόματο",
"BlocklistHelpText": "Αποτρέπει το Radarr από το να πάρει ξανά αυτόματα αυτήν την έκδοση",
"BypassDelayIfHighestQualityHelpText": "Παράκαμψη καθυστέρησης όταν η έκδοση έχει την υψηλότερη ενεργοποιημένη ποιότητα στο προφίλ ποιότητας με το προτιμώμενο πρωτόκολλο",
"ClickToChangeReleaseGroup": "Κάντε κλικ για να αλλάξετε την ομάδα κυκλοφορίας",
"DiscordUrlInSlackNotification": "Έχετε μια ρύθμιση ειδοποίησης Discord ως ειδοποίηση Slack. Ρυθμίστε το ως ειδοποίηση Discord για καλύτερη λειτουργικότητα. Οι ειδοποιήσεις που πραγματοποιούνται είναι: {0}",
"DownloadClientSortingCheckMessage": "Ο πελάτης λήψης {0} έχει ενεργοποιήσει την ταξινόμηση {1} για την κατηγορία του Radarr. Θα πρέπει να απενεργοποιήσετε την ταξινόμηση στο πρόγραμμα-πελάτη λήψης για να αποφύγετε προβλήματα εισαγωγής.",
"ImportListMissingRoot": "Λείπει ο ριζικός φάκελος για λίστες εισαγωγής: {0}",
"IndexerDownloadClientHelpText": "Καθορίστε ποιο πρόγραμμα-πελάτη λήψης χρησιμοποιείται για αρπαγές από αυτό το ευρετήριο",
"ManualImportSetReleaseGroup": "Μη αυτόματη εισαγωγή - Ορισμός ομάδας απελευθέρωσης",
"OriginalLanguage": "Γλώσσα Πρωτότυπου",
"OriginalTitle": "Πρωτότυπος τίτλος",
"AreYouSureYouWantToResetQualityDefinitions": "Είστε βέβαιοι ότι θέλετε να επαναφέρετε τους ορισμούς ποιότητας;",
"CollectionOptions": "Επιλογές συλλογής",
"CollectionShowDetailsHelpText": "Εμφάνιση κατάστασης και ιδιοτήτων συλλογής",
"CollectionShowOverviewsHelpText": "Εμφάνιση επισκοπήσεων συλλογής",
"CollectionShowPostersHelpText": "Εμφάνιση αφισών αντικειμένων συλλογής",
"Database": "Βάση δεδομένων",
"IndexerTagHelpText": "Χρησιμοποιήστε αυτό το ευρετήριο μόνο για ταινίες με τουλάχιστον μία αντίστοιχη ετικέτα. Αφήστε το κενό για χρήση με όλες τις ταινίες.",
"MovieAndCollection": "Ταινία και συλλογή",
"ApplicationURL": "Διεύθυνση URL εφαρμογής",
"Never": "Ποτέ",
"NotificationTriggersHelpText": "Επιλέξτε ποια συμβάντα θα ενεργοποιήσουν αυτήν την ειδοποίηση",
"InstanceName": "Όνομα παράδειγμα",
"PreferredProtocol": "Προτιμώμενο πρωτόκολλο",
"From": "απο",
"BypassDelayIfHighestQuality": "Παράκαμψη εάν είναι στην υψηλότερη ποιότητα",
"ShowPosters": "Εμφάνιση αφισών",
"RemotePathMappingCheckFilesWrongOSPath": "Ο πελάτης απομακρυσμένης λήψης {0} ανέφερε αρχεία στο {1} αλλά αυτή δεν είναι έγκυρη διαδρομή {2}. Ελέγξτε τις απομακρυσμένες αντιστοιχίσεις διαδρομής και κατεβάστε τις ρυθμίσεις πελάτη.",
"RemotePathMappingCheckLocalWrongOSPath": "Το πρόγραμμα-πελάτης τοπικής λήψης {0} τοποθετεί λήψεις στο {1} αλλά αυτή δεν είναι έγκυρη διαδρομή {2}. Ελέγξτε τις ρυθμίσεις του προγράμματος-πελάτη λήψης.",
"RemoveCompleted": "Κατάργηση Ολοκληρώθηκε",
"RemoveSelectedItems": "Αφαίρεση επιλεγμένων αντικειμένων",
"TmdbRating": "Αξιολόγηση TMDb",
"TmdbVotes": "Ψήφοι TMDb",
"RefreshCollections": "Ανανέωση Συλλογών",
"RemotePathMappingCheckFileRemoved": "Το αρχείο {0} καταργήθηκε εν μέρει κατά την επεξεργασία.",
"RemotePathMappingCheckFolderPermissions": "Το Radarr μπορεί να δει αλλά δεν έχει πρόσβαση στον κατάλογο λήψεων {0}. Πιθανό σφάλμα αδειών.",
"RemotePathMappingCheckGenericPermissions": "Το πρόγραμμα-πελάτης λήψης {0} τοποθετεί λήψεις στο {1} αλλά το Radarr δεν μπορεί να δει αυτόν τον κατάλογο. Ίσως χρειαστεί να προσαρμόσετε τα δικαιώματα του φακέλου.",
"RemotePathMappingCheckRemoteDownloadClient": "Ο πελάτης απομακρυσμένης λήψης {0} ανέφερε αρχεία στο {1} αλλά αυτός ο κατάλογος δεν φαίνεται να υπάρχει. Πιθανότατα λείπει η απομακρυσμένη χαρτογράφηση διαδρομής.",
"RemoveFailed": "Η αφαίρεση απέτυχε",
"ResetDefinitions": "Επαναφορά ορισμών",
"ResetQualityDefinitions": "Επαναφορά ορισμών ποιότητας",
"ResetTitles": "Επαναφορά τίτλων",
"RottenTomatoesRating": "Βαθμολογία ντομάτας",
"ShowOverview": "Εμφάνιση επισκόπησης",
"UnableToLoadCollections": "Δεν είναι δυνατή η φόρτωση των συλλογών",
"Started": "Ξεκίνησε",
"RefreshMonitoredIntervalHelpText": "Πόσο συχνά να ανανεώνετε τις παρακολουθούμενες λήψεις από προγράμματα-πελάτες λήψης, τουλάχιστον 1 λεπτό",
"RemotePathMappingCheckBadDockerPath": "Χρησιμοποιείτε docker. πελάτης λήψης {0} τοποθετεί λήψεις στο {1} αλλά αυτή δεν είναι έγκυρη διαδρομή {2}. Ελέγξτε τις απομακρυσμένες αντιστοιχίσεις διαδρομής και κατεβάστε τις ρυθμίσεις πελάτη.",
"RemotePathMappingCheckDockerFolderMissing": "Χρησιμοποιείτε docker. πελάτης λήψης {0} τοποθετεί λήψεις στο {1} αλλά αυτός ο κατάλογος δεν φαίνεται να υπάρχει μέσα στο κοντέινερ. Ελέγξτε τις απομακρυσμένες αντιστοιχίσεις διαδρομής και τις ρυθμίσεις όγκου κοντέινερ.",
"RemotePathMappingCheckDownloadPermissions": "Το Radarr μπορεί να δει αλλά δεν έχει πρόσβαση στην ταινία που έχει ληφθεί {0}. Πιθανό σφάλμα αδειών.",
"RemotePathMappingCheckFilesBadDockerPath": "Χρησιμοποιείτε docker. λήψη αρχείων πελάτη {0} που αναφέρθηκαν στο {1} αλλά αυτή δεν είναι έγκυρη διαδρομή {2}. Ελέγξτε τις απομακρυσμένες αντιστοιχίσεις διαδρομής και κατεβάστε τις ρυθμίσεις πελάτη.",
"RemotePathMappingCheckFilesGenericPermissions": "Λήψη αρχείων πελάτη {0} που αναφέρθηκαν στο {1} αλλά το Radarr δεν μπορεί να δει αυτόν τον κατάλογο. Ίσως χρειαστεί να προσαρμόσετε τα δικαιώματα του φακέλου.",
"RemotePathMappingCheckImportFailed": "Η Radarr απέτυχε να εισαγάγει μια ταινία. Ελέγξτε τα αρχεία καταγραφής σας για λεπτομέρειες.",
"RemotePathMappingCheckLocalFolderMissing": "Το πρόγραμμα-πελάτης απομακρυσμένης λήψης {0} τοποθετεί λήψεις στο {1} αλλά αυτός ο κατάλογος δεν φαίνεται να υπάρχει. Πιθανόν να λείπει ή να είναι εσφαλμένη η απομακρυσμένη αντιστοίχιση διαδρομής.",
"RemotePathMappingCheckWrongOSPath": "Το πρόγραμμα-πελάτης απομακρυσμένης λήψης {0} τοποθετεί λήψεις στο {1} αλλά αυτή δεν είναι έγκυρη διαδρομή {2}. Ελέγξτε τις απομακρυσμένες αντιστοιχίσεις διαδρομής και κατεβάστε τις ρυθμίσεις πελάτη.",
"RemoveDownloadsAlert": "Οι ρυθμίσεις κατάργησης μετακινήθηκαν στις μεμονωμένες ρυθμίσεις Download Client στον παραπάνω πίνακα.",
"RemoveSelectedItem": "Αφαίρεση επιλεγμένου αντικειμένου",
"ResetTitlesHelpText": "Επαναφέρετε τίτλους ορισμού καθώς και τιμές",
"RSSHelpText": "Θα χρησιμοποιηθεί όταν το Radarr αναζητά περιοδικά εκδόσεις μέσω RSS Sync",
"SearchOnAddCollectionHelpText": "Αναζητήστε ταινίες σε αυτήν τη συλλογή όταν προστεθούν στη βιβλιοθήκη",
"SelectReleaseGroup": "Επιλέξτε Ομάδα έκδοσης",
"SetReleaseGroup": "Ορισμός ομάδας απελευθέρωσης",
"SettingsThemeHelpText": "Αλλαγή του θέματος διεπαφής χρήστη εφαρμογής, το θέμα «Αυτόματο» θα χρησιμοποιήσει το Θέμα του λειτουργικού σας συστήματος για να ρυθμίσει τη λειτουργία Light ή Dark. Εμπνευσμένο από το Theme.Park",
"ScrollMovies": "Ταινίες με κύλιση",
"RemotePathMappingCheckFilesLocalWrongOSPath": "Τοπικό πρόγραμμα-πελάτη λήψης {0} ανέφερε αρχεία στο {1} αλλά αυτή δεν είναι έγκυρη διαδρομή {2}. Ελέγξτε τις ρυθμίσεις του προγράμματος-πελάτη λήψης.",
"SettingsTheme": "Θέμα",
"ShowCollectionDetails": "Εμφάνιση κατάστασης συλλογής",
"TaskUserAgentTooltip": "User-Agent που παρέχεται από την εφαρμογή που κάλεσε το API",
"TotalMovies": "Σύνολο ταινιών",
"Waiting": "Αναμονή",
"UpdateAvailable": "Νέα ενημέρωση είναι διαθέσιμη",
"SizeLimit": "Όριο μεγέθους"
}

View File

@@ -89,7 +89,7 @@
"Backups": "Backups",
"BeforeUpdate": "Before update",
"BindAddress": "Bind Address",
"BindAddressHelpText": "Valid IPv4 address or '*' for all interfaces",
"BindAddressHelpText": "Valid IP address, localhost or '*' for all interfaces",
"Blocklist": "Blocklist",
"Blocklisted": "Blocklisted",
"BlocklistHelpText": "Prevents Radarr from automatically grabbing this release again",
@@ -258,6 +258,7 @@
"DownloadClients": "Download Clients",
"DownloadClientSettings": "Download Client Settings",
"DownloadClientsSettingsSummary": "Download clients, download handling and remote path mappings",
"DownloadClientSortingCheckMessage": "Download client {0} has {1} sorting enabled for Radarr's category. You should disable sorting in your download client to avoid import issues.",
"DownloadClientStatusCheckAllClientMessage": "All download clients are unavailable due to failures",
"DownloadClientStatusCheckSingleClientMessage": "Download clients unavailable due to failures: {0}",
"DownloadClientUnavailable": "Download client is unavailable",
@@ -869,6 +870,7 @@
"RootFolders": "Root Folders",
"RottenTomatoesRating": "Tomato Rating",
"RSS": "RSS",
"RSSHelpText": "Will be used when Radarr periodically looks for releases via RSS Sync",
"RSSIsNotSupportedWithThisIndexer": "RSS is not supported with this indexer",
"RSSSync": "RSS Sync",
"RssSyncHelpText": "Interval in minutes. Set to zero to disable (this will stop all automatic release grabbing)",
@@ -928,7 +930,7 @@
"SettingsShowRelativeDates": "Show Relative Dates",
"SettingsShowRelativeDatesHelpText": "Show relative (Today/Yesterday/etc) or absolute dates",
"SettingsTheme": "Theme",
"SettingsThemeHelpText": "Change Application UI Theme, Inspired by Theme.Park",
"SettingsThemeHelpText": "Change Application UI Theme, 'Auto' Theme will use your OS Theme to set Light or Dark mode. Inspired by Theme.Park",
"SettingsTimeFormat": "Time Format",
"SettingsWeekColumnHeader": "Week Column Header",
"SettingsWeekColumnHeaderHelpText": "Shown above each column when week is the active view",

View File

@@ -100,7 +100,7 @@
"BackupIntervalHelpText": "Prowlarrin tietokannan ja asetusten automaattisen varmuuskopioinnin suoritusaikaväli.",
"AppDataDirectory": "AppData-kansio",
"BackupNow": "Varmuuskopioi nyt",
"BindAddressHelpText": "Toimiva IPv4-osoite tai '*' (tähti) kaikille yhteyksille.",
"BindAddressHelpText": "Toimiva IP-osoite, localhost tai '*' (tähti) kaikille yhteyksille.",
"Branch": "Kehityshaara",
"BuiltIn": "Sisäänrakennettu",
"CalendarOptions": "Kalenterin asetukset",
@@ -252,7 +252,7 @@
"Profiles": "Profiilit",
"ProxyType": "Välityspalvelimen tyyppi",
"PtpOldSettingsCheckMessage": "Seuraavat PassThePopcorn-tietolähteet sisältävät vanhentuneita asetuksia, jotka olisi syytä päivittää: {0}",
"QualitiesHelpText": "Listalla ylempänä olevia laatuja painotetaan. Saman ryhmän laadut ovat tasaveroisia. Valitse vain haluamasi laadut.",
"QualitiesHelpText": "Listalla ylempänä olevia laatuja painotetaan enemmän vaikkei niitä ole valittu. Samoissa ryhmissä olevat laadut ovat tasaveroisia. Valitse vain halutut laadut.",
"QualityProfileInUse": "Elokuvaan, listaan tai kokelmaan liitettyä laatuprofiilia ei voida poistaa.",
"QueueIsEmpty": "Jono on tyhjä",
"RadarrCalendarFeed": "Radarr-kalenterisyöte",
@@ -333,7 +333,7 @@
"CreateEmptyMovieFoldersHelpText": "Luo puuttuvat elokuvakansiot levyn tarkistuksen yhteydessä.",
"DeleteDownloadClient": "Poista lataustyökalu",
"ImportHeader": "Lisää Radarriin elokuvia tuomalla olemassa oleva, järjestetty kirjasto.",
"ImportMechanismHealthCheckMessage": "Ota valmiiden latausten käsittely käyttöön",
"ImportMechanismHealthCheckMessage": "Käytä valmiiden latausten käsittelyä",
"MinAvailability": "Pienin saatavuus",
"MovieIsUnmonitored": "Elokuvaa ei valvota",
"MovieNaming": "Elokuvien nimeäminen",
@@ -435,7 +435,7 @@
"StartTypingOrSelectAPathBelow": "Aloita kirjoitus tai valitse sijainti alta",
"StartupDirectory": "Käynnistyskansio",
"System": "Järjestelmä",
"SystemTimeCheckMessage": "Järjestelmän aika on heittä yli vuorokauden verran. Ajoitetut tehtävät eivät luultavasti toimi oikein ennen ajan korjausta.",
"SystemTimeCheckMessage": "Järjestelmän aika on pielessä yli vuorokauden. Ajoitetut tehtävät eivät luultavasti toimi oikein ennen sen korjausta.",
"Posters": "Julisteet",
"PosterSize": "Julisteen koko",
"TagCannotBeDeletedWhileInUse": "Tunnistetta ei voi poistaa, koska se on käytössä",
@@ -653,9 +653,9 @@
"IndexerPriority": "Tietolähteiden painotus",
"IndexerPriorityHelpText": "Tietolähteen painotus: 1 (korkein) - 50 (matalin). Oletusarvo on 25. Käytetään muutoin tasaveroisten julkaisujen sieppauspäätökseen. Kaikkia käytössä olevia tietolähteitä käytetään edelleen RSS-synkronointiin ja hakuun.",
"IndexerRssHealthCheckNoAvailableIndexers": "Kaikki RSS-tietolähteet ovat tilapaisesti tavoittamattomissa viimeaikaisten tietolähdevirheiden vuoksi.",
"IndexerRssHealthCheckNoIndexers": "Yhtään RSS-synkronointia käyttävää tietolähdettä ei ole käytettävissä, eikä Radarr tämän vuoksi sieppaa uusia julkaisuja automaattisesti.",
"IndexerRssHealthCheckNoIndexers": "Yhtään RSS-synkronointia käyttävää tietolähdettä ei ole käytettävissä, eikä uusia julkaisuja sen vuoksi siepata automaattisesti.",
"Indexers": "Tietolähteet",
"IndexerSearchCheckNoAutomaticMessage": "Ei hakemistoja, joissa automaattinen haku on käytössä, Radarr ei tarjoa automaattisia hakutuloksia",
"IndexerSearchCheckNoAutomaticMessage": "Automaattihaussa käytettäviä tietolähteitä ei ole käytettävissä, eikä automaattisia hakutuloksia ole tämän vuoksi saatavilla.",
"IndexerSearchCheckNoAvailableIndexersMessage": "Kaikki hakukelpoiset tietolähteet ovat tilapaisesti tavoittamattomissa viimeaikaisten tietolähdevirheiden vuoksi.",
"IndexerSettings": "Tietolähteiden asetukset",
"IndexerStatusCheckSingleClientMessage": "Tietolähteet eivät ole käytettävissä virheiden vuoksi: {0}",
@@ -750,7 +750,7 @@
"QualityDefinitions": "Laatumääritykset",
"QualityLimitsHelpText": "Rajoitukset suhteutetaan automaattisesti elokuvan kestoon.",
"QualityProfileDeleteConfirm": "Haluatko varmasti poistaa laatuprofiilin {0}",
"QualitySettingsSummary": "Laatumääritykset erilaisia sisältömuotoja ja teidostokokoja varten.",
"QualitySettingsSummary": "Laatumääritykset erilaisia sisältömuotoja ja tiedostokokoja varten.",
"RadarrSupportsAnyIndexer": "Radarr tukee Newznab- ja Torznab-yhteensopivien tietolähteiden ohella myös monia muita alla lueteltuja tietolähteitä.",
"RadarrSupportsCustomConditionsAgainstTheReleasePropertiesBelow": "Radarr tukee mukautettuja ehtoja perustuen julkaisujen alla oleviin ominaisuuksiin.",
"Ratings": "Arviot",
@@ -1146,5 +1146,13 @@
"TotalMovies": "Elokuvia yhteensä",
"ApplicationURL": "Sovelluksen URL-osoite",
"ApplicationUrlHelpText": "Sovelluksen ulkoinen URL-osoite, johon sisältyy http(s)://, portti ja URL-perusta.",
"PreferredProtocol": "Ensisijainen protokolla"
"PreferredProtocol": "Ensisijainen protokolla",
"ResetQualityDefinitions": "Palauta laatumääritykset",
"ResetTitles": "Palauta nimet",
"SettingsTheme": "Teema",
"SettingsThemeHelpText": "Vaihda sovelluksen käyttöliittymän ulkoasua. \"Automaattinen\" vaihtaa vaalean ja tumman tilan käyttöjärjestelmäsi teemaa vastaavaksi. Innoittanut Theme.Park.",
"AreYouSureYouWantToResetQualityDefinitions": "Haluatko varmasti palauttaa laatumääritykset?",
"ResetDefinitions": "Palauta määritykset",
"ResetTitlesHelpText": "Palauta määritysten nimet ja arvot.",
"RSSHelpText": "Käytetään etsittäessä julkaisuja RSS-syötteistä ajoitetusti."
}

View File

@@ -962,8 +962,8 @@
"MovieChat": "Movie Chat",
"MovieInvalidFormat": "Film : format non valide",
"MultiLanguage": "Multi-langue",
"Negate": "Nier",
"Negated": "Nié",
"Negate": "Inverser",
"Negated": "Inversé",
"NoListRecommendations": "Aucun élément de liste ou recommandation n'a été trouvé, pour commencer, vous voudrez ajouter un nouveau film, importer des films existants ou ajouter une liste.",
"OrganizeConfirm": "Voulez-vous vraiment organiser tous les fichiers dans les {0} films sélectionnés ?",
"OrganizeSelectedMovies": "Organiser les films sélectionnés",
@@ -1113,9 +1113,9 @@
"SelectReleaseGroup": "Sélectionner le groupe de publication",
"SetReleaseGroup": "Régler le groupe de publication",
"RefreshMonitoredIntervalHelpText": "Intervalle en minutes entre chaque vérification des téléchargements, minimum 1 minute",
"AllCollectionsHiddenDueToFilter": "Tous les films sont masqués en raison du filtre appliqué.",
"AllCollectionsHiddenDueToFilter": "Toutes les collections sont masquées en raison du filtre appliqué.",
"Collections": "Collections",
"MonitorMovies": "Surveiller le film",
"MonitorMovies": "Surveiller les films",
"NoCollections": "Aucun film trouvé, pour commencer, vous voudrez ajouter un nouveau film ou importer des films existants.",
"RssSyncHelpText": "Intervalle en minutes. Mettre à zéro pour désactiver (cela arrêtera tous les téléchargements automatiques)",
"CollectionsSelectedInterp": "{0} Collections(s) Sélectionnée(s)",

View File

@@ -159,7 +159,7 @@
"BranchUpdateMechanism": "A külső frissítési mechanizmus által használt ágazat",
"BranchUpdate": "Ágazattípus a Radarr frissítéseihez",
"Branch": "Ágazat",
"BindAddressHelpText": "Érvényes IPv4-cím, vagy „*” minden interfészhez",
"BindAddressHelpText": "Érvényes IP-cím, localhost vagy '*' minden interfészhez",
"BindAddress": "Kapcsolási Cím",
"BeforeUpdate": "Alkalmazásfrissítés előtt",
"Backups": "Biztonsági Mentés",
@@ -838,7 +838,7 @@
"EditCustomFormat": "Egyéni Formátumok szerkesztése",
"DoNotUpgradeAutomatically": "Ne frissítsen automatikusan",
"DoNotPrefer": "Nem preferált",
"DoneEditingGroups": "Kész szerkesztő csoportok",
"DoneEditingGroups": "A csoportok szerkesztése kész",
"Donations": "Adományok",
"DockerUpdater": "A Frissítéshez frissítenie kell a Docker tárolót",
"Discord": "Discord",
@@ -965,7 +965,7 @@
"QueueIsEmpty": "A várakozási sor üres",
"QualityProfileInUse": "A filmhez, listához vagy gyűjteményhez csatolt minőségi profil nem törölhető",
"QualityLimitsHelpText": "A korlátozások automatikusan beállítódnak a film hossza szerint.",
"QualitiesHelpText": "A jobb minőség a listában jobban preferált. Ugyanazon minőségek a csoportban egyenlőek. Csak a bejelölt minőségek a kívánt minőségek",
"QualitiesHelpText": "A listán magasabb minőségek előnyösebbek, még akkor is, ha nincs bejelölve. Ugyanazon csoporton belül a tulajdonságok egyenlőek. Csak ellenőrzött minőségek szükségesek",
"Qualities": "Minőségek",
"PrioritySettings": "Prioritás: {0}",
"PreviewRenameHelpText": "Tipp: Átnevezés előnézetéhez... válassza a 'Visszavonást' majd kattintson a film címére és használja a",
@@ -1149,9 +1149,11 @@
"PreferredProtocol": "Preferált protokoll",
"AreYouSureYouWantToResetQualityDefinitions": "Biztos visszaállítod a minőségi definíciókat?",
"SettingsTheme": "Téma",
"SettingsThemeHelpText": "Kezelőfelület témájának módosítása, a Theme.Park jóvoltából",
"SettingsThemeHelpText": "Változtasd meg az alkalmazás felhasználói felület témáját, az „Auto” téma az operációs rendszer témáját használja a Világos vagy Sötét mód beállításához. A Theme.Park ihlette",
"ResetDefinitions": "Definíciók visszaállítása",
"ResetQualityDefinitions": "Állítsd vissza a minőségi meghatározásokat",
"ResetTitles": "Címek visszaállítása",
"ResetTitlesHelpText": "A definíciócímek és értékek visszaállítása"
"ResetTitlesHelpText": "A definíciócímek és értékek visszaállítása",
"RSSHelpText": "Akkor használatos, amikor a Radarr rendszeresen keres kiadásokat az RSS Sync segítségével",
"DownloadClientSortingCheckMessage": "A(z) {0} letöltési kliensben engedélyezve van a(z) {1} rendezés a Radarr kategóriájához. Az importálási problémák elkerülése érdekében le kell tiltania a rendezést a letöltési kliensben."
}

View File

@@ -59,7 +59,7 @@
"AppDataLocationHealthCheckMessage": "L'aggiornamento non sarà possibile per evitare la cancellazione di AppData durante l'aggiornamento",
"Analytics": "Analitica",
"Added": "Aggiunto",
"About": "Informazioni",
"About": "Info",
"Year": "Anno",
"Week": "Settimana",
"Updates": "Aggiornamenti",
@@ -189,7 +189,7 @@
"ChooseAnotherFolder": "Scegli un'altra Cartella",
"Cast": "Cast",
"Calendar": "Calendario",
"BackupNow": "Effettua il Backup adesso",
"BackupNow": "Esegui backup ora",
"Backup": "Backup",
"All": "Tutti",
"Agenda": "Agenda",
@@ -220,12 +220,12 @@
"ChangeFileDate": "Cambiare la Data del File",
"CertificationCountryHelpText": "Seleziona il Paese per le Certificazioni dei Film",
"CertificationCountry": "Paese di Certificazione",
"CertificateValidationHelpText": "Cambia quanto è rigorosa la convalida del certificato HTTPS. Non cambiare a meno che tu non comprenda i rischi.",
"CertificateValidationHelpText": "Cambia quanto rigorosamente vengono validati i certificati HTTPS. Non cambiare senza conoscerne i rischi.",
"CertificateValidation": "Convalida del Certificato",
"Cancel": "Annulla",
"BypassProxyForLocalAddresses": "Evita il Proxy per gli Indirizzi Locali",
"Branch": "Ramo",
"BindAddressHelpText": "Indirizzo IPv4 valido o '*' per tutte le interfacce",
"BindAddressHelpText": "Indirizzo IPV4 valido o '*' per tutte le interfacce di rete",
"BindAddress": "Indirizzo di Bind",
"Backups": "I Backup",
"BackupRetentionHelpText": "I backup automatici più vecchi del periodo di conservazione verranno eliminati automaticamente",
@@ -261,7 +261,7 @@
"TotalSpace": "Spazio Totale",
"Title": "Titolo",
"Time": "Ora",
"TestAll": "Testa Tutti",
"TestAll": "Prova Tutti",
"Test": "Test",
"TableOptionsColumnsMessage": "Scegli quali colonne rendere visibili ed il loro ordine",
"TableOptions": "Opzioni della tabella",
@@ -452,7 +452,7 @@
"RadarrSupportsAnyRSSMovieListsAsWellAsTheOneStatedBelow": "Radarr supporta qualunque Lista di film RSS, cosi come le altre sotto.",
"RadarrSupportsAnyDownloadClient": "Radarr supporta qualunque client di download che usi gli standard Newznab, cosi come gli altri client sotto.",
"QuickImport": "Sposta automaticamente",
"Queued": "Messo in coda",
"Queued": "In coda",
"QualitySettings": "Impostazione di Qualità",
"QualityProfileDeleteConfirm": "Sicuro di voler cancellare il profilo di qualità {0}",
"QualityCutoffHasNotBeenMet": "Il qualità di taglio non è stata raggiunta",
@@ -713,8 +713,8 @@
"TimeFormat": "Formato orario",
"ThisConditionMatchesUsingRegularExpressions": "Questa condizione si applica usando espressione regolari. Nota che i caratteri {0} hanno significati speciali e devono essere evitati con un {1}",
"TestAllLists": "Testa tutte le liste",
"TestAllIndexers": "Testa tutti gli Indicizzatori",
"TestAllClients": "Testa Tutti i Client",
"TestAllIndexers": "Prova tutti gli indicizzatori",
"TestAllClients": "Testa tutti i client",
"TagsHelpText": "Si applica ai film con almeno un tag corrispondente",
"TagIsNotUsedAndCanBeDeleted": "L'etichetta non è in uso e può essere eliminata",
"TagCannotBeDeletedWhileInUse": "Non può essere cancellato mentre è in uso",
@@ -793,7 +793,7 @@
"MovieDetailsPreviousMovie": "Dettagli del film: film precedente",
"FocusSearchBox": "Evidenzia casella di ricerca",
"CloseCurrentModal": "Chiudi la Modale Attuale",
"AcceptConfirmationModal": "Acetta Conferma Modale",
"AcceptConfirmationModal": "Accetta Conferma Modale",
"StartSearchForMissingMovie": "Avvia ricerca film mancanti",
"StartProcessing": "Avvia Lavorazione",
"StartImport": "Avvia Importazione",
@@ -842,7 +842,7 @@
"AddQualityProfile": "Aggiungi Profilo Qualità",
"AddNotification": "Aggiungi Notifica",
"AddedToDownloadQueue": "Aggiunto alla coda di download",
"AddDownloadClient": "Aggiungi Client di Download",
"AddDownloadClient": "Aggiungi Downloader",
"AddCustomFormat": "Aggiungi Formato Personalizzato",
"Add": "Aggiungi",
"ImportLibrary": "Importazione della libreria",
@@ -1091,11 +1091,21 @@
"ClickToChangeReleaseGroup": "Clicca per cambiare gruppo di rilascio",
"DiscordUrlInSlackNotification": "Hai una notifica Discord configurata come notifica Slack. Configurala come notifica Discord per una miglior funzionalità. Le notifiche interessate sono: {0}",
"Duration": "Durata",
"InstanceNameHelpText": "Nome dell'istanza nella scheda e per il nome dell'applicazione Syslog",
"InstanceNameHelpText": "Nome istanza nella scheda e per il nome dell'app nel Syslog",
"InstanceName": "Nome Istanza",
"AllCollectionsHiddenDueToFilter": "Tutti i film sono nascosti a causa del filtro applicato.",
"Collections": "Collezione",
"MonitorMovies": "Monitora Film",
"NoCollections": "Nessun film trovato, per iniziare ti consigliamo di aggiungere un nuovo film o importarne alcuni esistenti.",
"RssSyncHelpText": "Intervallo in minuti. Imposta zero per disabilitarlo (ciò fermerà il recupero automatico di tutte le release)"
"RssSyncHelpText": "Intervallo in minuti. Imposta zero per disabilitarlo (ciò fermerà il recupero automatico di tutte le release)",
"ApplicationURL": "URL Applicazione",
"ApplicationUrlHelpText": "L'URL esterno di questa applicazione, incluso http(s)://, porta e URL base",
"CollectionOptions": "Opzioni della Collezione",
"CollectionShowDetailsHelpText": "Mostra lo stato e le proprietà della collezione",
"Started": "Iniziato",
"AreYouSureYouWantToResetQualityDefinitions": "Sei sicuro di voler ripristinare le definizioni della qualità?",
"ChooseImportMode": "Selezionare Metodo di Importazione",
"EditCollection": "Modifica Connessione",
"SettingsThemeHelpText": "Cambia il Tema dell'interfaccia dellapplicazione, il Tema 'Auto' userà il suo Tema di Sistema per impostare la modalità Chiara o Scura. Ispirato da {0}",
"PreferredProtocol": "Protocollo Preferito"
}

View File

@@ -372,7 +372,7 @@
"CertificateValidation": "Certificaat Validatie",
"BypassProxyForLocalAddresses": "Omzeil Proxy voor Lokale Adressen",
"Branch": "Branch",
"BindAddressHelpText": "Geldig IPv4-adres of '*' voor alle interfaces",
"BindAddressHelpText": "Geldig IP-adres, localhost of '*' voor alle interfaces",
"DeleteBackup": "Verwijder Veiligheidskopie",
"BackupIntervalHelpText": "Tussentijd voor automatische back-up",
"Backups": "Veiligheidskopieën",
@@ -723,7 +723,7 @@
"HaveNotAddedMovies": "U heeft nog geen films toegevoegd, wilt u eerst enkele of al uw films importeren?",
"ForMoreInformationOnTheIndividualIndexers": "Voor meer informatie over de individuele indexeerders, klik op de info knoppen.",
"ForMoreInformationOnTheIndividualImportListsClinkOnTheInfoButtons": "Voor meer informatie over de individuele lijsten, klik op de info knoppen.",
"ForMoreInformationOnTheIndividualDownloadClients": "Voor meer informatie over de individuele downloaders, klik op de info knoppen.",
"ForMoreInformationOnTheIndividualDownloadClients": "Voor meer informatie over de individuele download applicaties, klik op de info knoppen.",
"FailedLoadingSearchResults": "Laden van zoekresultaten is mislukt, gelieve opnieuw te proberen.",
"ExtraFileExtensionsHelpTexts1": "Komma gescheiden lijst met extra bestanden om te importeren (.nfo zal als .nfo-orig worden geïmporteerd)",
"CouldNotFindResults": "Kon geen resultaten vinden voor '{0}'",
@@ -1045,7 +1045,7 @@
"DeleteFileLabel": "Verwijder {0} Film Bestanden",
"UnableToAddRootFolder": "Kon hoofdmappen niet inladen",
"UpdateAvailable": "Nieuwe update is beschikbaar",
"From": "Van",
"From": "van",
"RemotePathMappingCheckDownloadPermissions": "Radarr kan gedownloade film {0} zien, maar niet openen. Waarschijnlijk fout met machtigingen.",
"RemotePathMappingCheckFileRemoved": "Bestand {0} is halverwege de verwerking verwijderd.",
"RemotePathMappingCheckFilesBadDockerPath": "U gebruikt docker; download client {0} gerapporteerde bestanden in {1} maar dit is geen geldig {2} pad. Controleer uw externe padtoewijzingen en download clientinstellingen.",
@@ -1072,7 +1072,7 @@
"BlocklistRelease": "Uitgave op blokkeerlijst zetten",
"RemoveFromBlocklist": "Verwijder van zwarte lijst",
"UnableToLoadBlocklist": "Kon zwarte lijst niet laden",
"Blocklisted": "Blokkeerlijst",
"Blocklisted": "Geblokeerde lijst",
"AreYouSureYouWantToRemoveSelectedItemFromQueue": "Ben je zeker dat je {1} item{2} uit de wachtrij wilt verwijderen?",
"BlocklistReleases": "Uitgave op blokkeerlijst zetten",
"LocalPath": "Lokaal Pad",
@@ -1113,5 +1113,8 @@
"ChooseImportMode": "Kies Importmodus",
"CollectionOptions": "Collectieopties",
"CollectionShowDetailsHelpText": "Collectie status en details weergeven",
"CollectionShowOverviewsHelpText": "Collectieoverzicht weergeven"
"CollectionShowOverviewsHelpText": "Collectieoverzicht weergeven",
"EditCollection": "Bewerk collectie",
"BypassDelayIfHighestQualityHelpText": "Vertraging negeren wanneer een release de hoogst mogelijke kwaliteit heeft in het kwaliteitsprofiel van het geprefereerde protocol",
"CollectionShowPostersHelpText": "Posters van de collectie tonen"
}

View File

@@ -19,7 +19,7 @@
"IndexerStatusCheckAllClientMessage": "Todos os indexadores estão indisponíveis devido a falhas",
"IndexersSettingsSummary": "Indexadores e restrições de lançamento",
"IndexerSettings": "Configurações do indexador",
"IndexerSearchCheckNoInteractiveMessage": "Nenhum indexador disponível com a Pesquisa interativa ativada, o Radarr não fornecerá nenhum resultado de pesquisa interativa",
"IndexerSearchCheckNoInteractiveMessage": "Nenhum indexador disponível com a Pesquisa Interativa habilitada, o Radarr não fornecerá nenhum resultado de pesquisa interativa",
"IndexerSearchCheckNoAvailableIndexersMessage": "Todos os indexadores com capacidade de pesquisa estão temporariamente indisponíveis devido a erros recentes do indexador",
"IndexerSearchCheckNoAutomaticMessage": "Nenhum indexador disponível com a Pesquisa automática habilitada, o Radarr não fornecerá nenhum resultado de pesquisa automática",
"Indexers": "Indexadores",
@@ -84,7 +84,7 @@
"HealthNoIssues": "Nenhum problema com sua configuração",
"Health": "Integridade",
"HaveNotAddedMovies": "Você ainda não adicionou nenhum filme, deseja importar alguns ou todos os seus filmes primeiro?",
"HardlinkCopyFiles": "Vínculo real/Copiar arquivos",
"HardlinkCopyFiles": "Hardlink/Copiar arquivos",
"Group": "Grupo",
"GrabSelected": "Obter selecionado",
"GrabReleaseMessageText": "O Radarr não conseguiu determinar a qual filme este lançamento está relacionado. O Radarr pode não conseguir importar automaticamente este lançamento. Quer obter \"{0}\"?",
@@ -174,7 +174,7 @@
"EditListExclusion": "Editar exclusão da lista",
"Edition": "Edição",
"EditIndexer": "Editar indexador",
"EditGroups": "Editar grupos",
"EditGroups": "Editar Grupos",
"EditDelayProfile": "Editar perfil de atraso",
"EditCustomFormat": "Editar formato personalizado",
"Edit": "Editar",
@@ -202,7 +202,7 @@
"DownloadClient": "Cliente de download",
"DoNotUpgradeAutomatically": "Não atualizar automaticamente",
"DoNotPrefer": "Não preferir",
"DoneEditingGroups": "Edição de grupos concluída",
"DoneEditingGroups": "Concluído a Edição de Grupos",
"Donations": "Doações",
"DockerUpdater": "atualizar o contêiner do Docker para receber a atualização",
"Docker": "Docker",
@@ -267,7 +267,7 @@
"CustomFormatUnknownCondition": "Condição de formato personalizado \"{0}\" desconhecida",
"CustomFormatsSettingsSummary": "Configurações e formatos personalizados",
"CustomFormatsSettings": "Configurações de formatos personalizados",
"CustomFormatScore": "Pontuação de formato personalizado",
"CustomFormatScore": "Pontuação do Formato Personalizado",
"CustomFormats": "Formatos personalizados",
"CustomFormatJSON": "JSON de formato personalizado",
"CustomFormatHelpText": "O Radarr pontua cada versão usando a soma das pontuações para formatos personalizados encontrados. Se uma nova versão tiver melhor pontuação, com a mesma qualidade ou melhor, o Radarr o capturará.",
@@ -340,7 +340,7 @@
"BranchUpdateMechanism": "Ramificação usada pelo mecanismo de atualização externo",
"BranchUpdate": "Ramificação para atualização do Radarr",
"Branch": "Ramo",
"BindAddressHelpText": "Endereço IPv4 Válido ou '*' para todas as interfaces",
"BindAddressHelpText": "Endereço IP válido, localhost ou '*' para todas as interfaces",
"BindAddress": "Fixar Endereço",
"BeforeUpdate": "Antes da atualização",
"Backups": "Backups",
@@ -402,7 +402,7 @@
"MinimumLimits": "Limites mínimos",
"MinimumFreeSpaceWhenImportingHelpText": "Impedir a importação se deixar menos do que esta quantidade de espaço em disco disponível",
"MinimumFreeSpace": "Mínimo de espaço livre",
"MinimumCustomFormatScore": "Pontuação mínima de formato personalizado",
"MinimumCustomFormatScore": "Pontuação Mínima do Formato Personalizado",
"MinimumAgeHelpText": "Somente Usenet: tempo de vida mínimo, em minutos, dos NZBs, para que sejam capturados. Use isto para dar às novas versões tempo de propagar-se em seu fornecedor de Usenet.",
"MinimumAge": "Tempo de vida mínimo",
"MinimumAvailability": "Disponibilidade mínima",
@@ -484,7 +484,7 @@
"AddMovies": "Adicionar filmes",
"AddMovie": "Adicionar filme",
"AddListExclusion": "Adicionar exclusão à lista",
"AddList": "Adicionar à Lista",
"AddList": "Adicionar Lista",
"AddingTag": "Adicionando etiqueta",
"AddIndexer": "Adicionar indexador",
"AddImportExclusionHelpText": "Impedir a adição do filme ao Radarr por listas",
@@ -752,7 +752,7 @@
"Restrictions": "Restrições",
"RestoreBackup": "Restaurar backup",
"Restore": "Restaurar",
"RestartRequiredHelpTextWarning": "Requer reinicio para fazer efeito",
"RestartRequiredHelpTextWarning": "Requer reinicialização para ter efeito",
"RestartRadarr": "Reiniciar o Radarr",
"RestartNow": "Reiniciar agora",
"Restart": "Reiniciar",
@@ -854,7 +854,7 @@
"QualityDefinitions": "Definições de qualidade",
"QualityCutoffHasNotBeenMet": "Limite de qualidade não atingido",
"Quality": "Qualidade",
"QualitiesHelpText": "As qualidades mais altas na lista são mais preferidas, mesmo que não estejam marcadas. As qualidades dentro do mesmo grupo são iguais. Apenas qualidades marcadas são desejadas",
"QualitiesHelpText": "As qualidades mais altas na lista são mais preferidas, mesmo que não sejam verificadas. As qualidades dentro do mesmo grupo são iguais. Somente qualidades verificadas são desejadas",
"Qualities": "Qualidades",
"PublishedDate": "Data de publicação",
"PtpOldSettingsCheckMessage": "As configurações dos seguintes indexadores do PassThePopcorn são obsoletas e devem ser atualizadas: {0}",
@@ -868,7 +868,7 @@
"Proxy": "Proxy",
"ProtocolHelpText": "Escolha que protocolo(s) utilizar e qual o preferido ao escolher entre versões iguais",
"Protocol": "Protocolo",
"Proper": "Proper",
"Proper": "Apropriado",
"Progress": "Progresso",
"ProfilesSettingsSummary": "Perfis de qualidade, idioma e atraso",
"Profiles": "Perfis",
@@ -923,7 +923,7 @@
"OnRenameHelpText": "Ao renomear",
"OnRename": "Ao Renomear",
"OnlyUsenet": "Apenas Usenet",
"OnlyTorrent": "Apenas torrents",
"OnlyTorrent": "Apenas Torrents",
"OnLatestVersion": "A versão mais recente do Radarr já está instalada",
"OnImport": "Ao importar",
"OnHealthIssueHelpText": "Ao ter problema de integridade",
@@ -1053,7 +1053,7 @@
"RemotePathMappingCheckImportFailed": "O Radarr não conseguiu importar um filme. Verifique os logs para saber mais.",
"RemotePathMappingCheckFileRemoved": "O arquivo {0} foi removido no meio do processamento.",
"RemotePathMappingCheckDownloadPermissions": "O Radarr pode ver, mas não pode acessar o filme baixado {0}. Provável erro de permissões.",
"RemotePathMappingCheckGenericPermissions": "O cliente de download {0} coloca downloads em {1} mas o Radarr não pode ver este diretório. Pode ser necessário ajustar as permissões da pasta.",
"RemotePathMappingCheckGenericPermissions": "O cliente de download {0} coloca os downloads em {1}, mas o Radarr não pode ver este diretório. Pode ser necessário ajustar as permissões da pasta.",
"RemotePathMappingCheckWrongOSPath": "O cliente de download remoto {0} coloca downloads em {1}, mas este não é um caminho {2} válido. Revise seus mapeamentos de caminho remoto e baixe as configurações do cliente.",
"RemotePathMappingCheckLocalWrongOSPath": "O cliente de download local {0} coloca downloads em {1}, mas este não é um caminho {2} válido. Revise as configurações do seu cliente de download.",
"RemotePathMappingCheckLocalFolderMissing": "O cliente de download remoto {0} coloca downloads em {1}, mas esse diretório parece não existir. Mapeamento de caminho remoto provavelmente ausente ou incorreto.",
@@ -1078,7 +1078,7 @@
"AreYouSureYouWantToRemoveSelectedItemFromQueue": "Você tem certeza de que deseja remover {0} item{1} da fila?",
"BlocklistReleases": "Lançamento na lista de bloqueio",
"RemoveSelectedItems": "Remover Itens Selecionados",
"IndexerTagHelpText": "Só use este indexador para filmes com ao menos uma etiqueta correspondente. Deixe em branco para usar com todos os filmes.",
"IndexerTagHelpText": "Use este indexador apenas para filmes com pelo menos uma etiqueta correspondente. Deixe em branco para usar com todos os filmes.",
"RemoveSelectedItem": "Remover Item Selecionado",
"RemoveFailed": "Falha na Remoção",
"RemoveCompleted": "Remover Completos",
@@ -1087,13 +1087,13 @@
"OnApplicationUpdateHelpText": "Na Atualização do Aplicativo",
"DiscordUrlInSlackNotification": "Você tem uma notificação do Discord configurado como uma notificação do Slack. Definir isso como uma notificação do Discord para melhor funcionalidade. Com efeito, notificações são: {0}",
"AnnouncedMsg": "Filme foi anunciado",
"IndexerDownloadClientHelpText": "Especificar em que cliente de download é usado para baixar deste indexador",
"IndexerDownloadClientHelpText": "Especificar qual cliente de download é utilizado para baixar a partir deste indexador",
"LocalPath": "Caminho Local",
"ManualImportSetReleaseGroup": "Importar Manual - Definir Grupo de Lançamento",
"SelectLanguages": "Selecionar Idiomas",
"SelectReleaseGroup": "Selecionar Grupo do Lançamento",
"SetReleaseGroup": "Definir Grupo do Lançamento",
"ClickToChangeReleaseGroup": "Clique para mudar o grupo do lançamento",
"ClickToChangeReleaseGroup": "Clique para alterar o grupo do lançamento",
"Filters": "Filtros",
"RemotePath": "Caminho Remoto",
"SizeLimit": "Tamanho Limite",
@@ -1147,11 +1147,13 @@
"ApplicationURL": "URL do Aplicativo",
"ApplicationUrlHelpText": "A URL externa deste aplicativo, incluindo http(s)://, porta e base da URL",
"PreferredProtocol": "Protocolo Preferido",
"SettingsThemeHelpText": "Alterar o tema da interface do usuário do aplicativo, inspirado no Theme.Park",
"SettingsThemeHelpText": "Alterar o tema da interface do usuário do aplicativo, o tema 'Auto' usará o tema do sistema operacional para definir o modo Claro ou Escuro. Inspirado por Theme.Park",
"ResetDefinitions": "Redefinir definições",
"ResetQualityDefinitions": "Redefinir Definições de Qualidade",
"ResetTitles": "Redefinir Títulos",
"ResetTitlesHelpText": "Redefinir títulos de definição, bem como valores",
"SettingsTheme": "Tema",
"AreYouSureYouWantToResetQualityDefinitions": "Tem certeza de que deseja redefinir as definições de qualidade?"
"AreYouSureYouWantToResetQualityDefinitions": "Tem certeza de que deseja redefinir as definições de qualidade?",
"RSSHelpText": "Será usado quando o Radarr procurar periodicamente lançamentos via RSS Sync",
"DownloadClientSortingCheckMessage": "O cliente de download {0} tem classificação {1} habilitada para a categoria do Radarr. Você deve desativar a classificação em seu cliente de download para evitar problemas de importação."
}

View File

@@ -170,7 +170,7 @@
"ExcludeTitle": "Исключить {0}? Radarr не будет автоматически добавлять сканируя лист.",
"InCinemasMsg": "Фильмы в кинотеатрах",
"IncludeRecommendationsHelpText": "Включить в отображении найденного фильмы рекомендованные Radar",
"IndexerPriorityHelpText": "Приоритет индексатора от 1 (самый высокий) до 50 (самый низкий). По умолчанию: 25. Используется при захвате выпусков в качестве средства разрешения конфликтов для равных в остальном выпусков, Radarr по-прежнему будет использовать все включенные индексаторы для синхронизации и поиска RSS.",
"IndexerPriorityHelpText": "Приоритет индексатора от 1 (самый высокий) до 50 (самый низкий). По умолчанию: 25. Используется при захвате выпусков в качестве средства разрешения конфликтов для равных в остальном выпусков, Radarr по-прежнему будет использовать все включенные индексаторы для синхронизации и поиска RSS",
"IndexerSearchCheckNoAvailableIndexersMessage": "Все индексаторы с возможностью поиска временно выключены из-за ошибок",
"KeyboardShortcuts": "Горячие клавиши",
"CantFindMovie": "Почему я не могу найти фильм?",
@@ -330,7 +330,7 @@
"Connect": "Подключить",
"Connections": "Соединения",
"CompletedDownloadHandling": "Обработка завершенных скачиваний",
"IndexerSearchCheckNoInteractiveMessage": "Нет доступных индексаторов с интерактивным поиском, Radarr не будет предоставлять результаты интерактивного поиска",
"IndexerSearchCheckNoInteractiveMessage": "Нет доступных индексаторов с включенным интерактивным поиском, Radarr не будет предоставлять результаты интерактивного поиска",
"IndexersSettingsSummary": "Ограничения для индексаторов и релизов",
"Language": "Язык",
"ImportListStatusCheckAllClientMessage": "Все листы недоступны из-за ошибок",
@@ -421,7 +421,7 @@
"QualitySettingsSummary": "Качественные размеры и наименования",
"QualitySettings": "Настройки качества",
"QualityProfiles": "Профили качества",
"QualityProfileInUse": "Невозможно удалить профиль качества прикрепленный к фильму",
"QualityProfileInUse": "Невозможно удалить профиль качества, прикрепленный к фильму, списку или коллекции",
"QualityProfileDeleteConfirm": "Вы действительно хотите удалить профиль качества {0}",
"QualityProfile": "Профиль качества",
"QualityOrLangCutoffHasNotBeenMet": "Не соблюдено ограничение по качеству или языку",
@@ -429,7 +429,7 @@
"QualityDefinitions": "Определения качества",
"QualityCutoffHasNotBeenMet": "Порог качества не соблюден",
"Quality": "Качество",
"QualitiesHelpText": "Качества, расположенные выше в списке, более предпочтительны. Качества внутри одной группы равны. Требуются только отмеченные качества",
"QualitiesHelpText": "Качества, расположенные выше в списке, являются более предпочтительными, даже если они не отмечены. Качества внутри одной группы равны. Требуются только отмеченные качества",
"Qualities": "Качества",
"PublishedDate": "Дата публикации",
"PtpOldSettingsCheckMessage": "Следующие индексаторы PassThePopcorn устарели и должны быть обновлены: {0}",
@@ -747,7 +747,7 @@
"BranchUpdateMechanism": "Ветвь, используемая внешним механизмом обновления",
"BranchUpdate": "Ветвь для обновления Radarr",
"Branch": "Ветка",
"BindAddressHelpText": "Действительный IPv4-адрес или '*' для всех интерфейсов",
"BindAddressHelpText": "Действительный IP-адрес, локальный адрес или '*' для всех интерфейсов",
"BackupFolderHelpText": "Относительные пути будут в каталоге AppData Radarr",
"AvailabilityDelayHelpText": "Время до или после доступной даты для поиска фильма",
"AvailabilityDelay": "Задержка доступности",
@@ -1047,7 +1047,7 @@
"RemotePathMappingCheckRemoteDownloadClient": "Удалённый клиент загрузки {0} сообщил о файлах в {1}, но эта директория, похоже, не существует. Вероятно, отсутствует сопоставление удаленных путей.",
"RemotePathMappingCheckLocalWrongOSPath": "Локальный клиент загрузки {0} загружает файлы в {1}, но это не правильный путь {2}. Проверьте настройки клиента загрузки.",
"RemotePathMappingCheckLocalFolderMissing": "Удалённый клиент загрузки {0} загружает файлы в {1}, но эта директория, похоже, не существует. Вероятно, отсутствует или неправильно указан удаленный путь.",
"RemotePathMappingCheckGenericPermissions": "Клиент загрузки {0} загружает файлы в {1}, но Radarr не может найти эту директорию. Возможно, вам нужно настроить права доступа к данной директории.",
"RemotePathMappingCheckGenericPermissions": "Клиент загрузки {0} загружает файлы в {1}, но Radarr не может видеть этот каталог. Возможно, вам нужно настроить права доступа к данной директории.",
"RemotePathMappingCheckFilesBadDockerPath": "Вы используете docker; клиент загрузки {0} сообщил о файлах в {1}, но это не корректный путь {2}. Проверьте правильность указанного пути и настройки клиента загрузки.",
"RemotePathMappingCheckImportFailed": "Radarr не удалось импортировать фильм. Проверьте ваши логи для более подробной информации.",
"RemotePathMappingCheckFolderPermissions": "Radarr видит директорию загрузки {0}, но не имеет доступа к ней. Возможно, ошибка в правах доступа.",
@@ -1108,7 +1108,7 @@
"AllCollectionsHiddenDueToFilter": "Все фильмы скрыты в соответствии с фильтром.",
"Collections": "Коллекции",
"MonitorMovies": "Отслеживать фильм",
"NoCollections": "Коллекции не найдены. Для начала вам нужно добавить новый фильм или импортировать несколько существующих.",
"NoCollections": "Коллекции не найдены. Для начала вам нужно добавить новый фильм или импортировать несколько существующих",
"CollectionOptions": "Параметры коллекции",
"CollectionShowDetailsHelpText": "Показать статус и свойства коллекции",
"CollectionShowOverviewsHelpText": "Показать обзоры коллекций",
@@ -1142,5 +1142,18 @@
"RottenTomatoesRating": "Tomato рейтинг",
"Started": "Запущено",
"Database": "База данных",
"From": "из"
"From": "из",
"AreYouSureYouWantToResetQualityDefinitions": "Вы уверены, что хотите сбросить определения качества?",
"ResetDefinitions": "Сбросить определения",
"ResetQualityDefinitions": "Сбросить определения качества",
"ResetTitles": "Сбросить заголовки",
"ResetTitlesHelpText": "Сбросить заголовки определений, а также значения",
"RSSHelpText": "Будет использоваться, когда Radarr будет периодически искать выпуски через RSS Sync",
"SettingsTheme": "Тема",
"SettingsThemeHelpText": "Измените тему пользовательского интерфейса приложения, тема «Авто» будет использовать тему вашей ОС для установки светлого или темного режима. Вдохновленный Theme.Park",
"PreferredProtocol": "Предпочтительный протокол",
"ApplicationURL": "URL-адрес приложения",
"ApplicationUrlHelpText": "Внешний URL-адрес этого приложения, включая http(s)://, порт и базовый URL-адрес",
"ScrollMovies": "Прокрутите фильмы",
"DownloadClientSortingCheckMessage": "В загрузочном клиенте {0} включена {1} сортировка для категории Radarr. Вам следует отключить сортировку в вашем клиенте загрузки, чтобы избежать проблем с импортом."
}

View File

@@ -170,5 +170,85 @@
"CloneIndexer": "Klonovať indexer",
"CloneProfile": "Klonovať profil",
"Close": "Zatvoriť",
"CloseCurrentModal": "Zatvoriť aktuálne okno"
"CloseCurrentModal": "Zatvoriť aktuálne okno",
"Filters": "Filtre",
"Reload": "Obnoviť",
"Trakt": "Trakt",
"UI": "UI",
"Crew": "Štáb",
"Deleted": "Vymazať",
"DownloadClient": "Klient na sťahovanie",
"Filter": "Filter",
"Languages": "jazyk",
"Posters": "Plagáty",
"Connections": "Spojenia",
"DelayProfile": "Profil oneskorenia",
"Grab": "Grab",
"Language": "jazyk",
"DelayProfiles": "Profil oneskorenia",
"Delete": "Vymazať",
"Events": "Udalosť",
"Password": "Heslo",
"Peers": "Peeri",
"Remove": "Odstrániť",
"Required": "Vyžadovať",
"Details": "podrobnosti",
"DownloadClients": "Klient na sťahovanie",
"Torrents": "Torrenty",
"ICalFeed": "iCal Feed",
"Indexer": "Indexer",
"Port": "Port",
"QualityProfile": "Profil kvality",
"QualityProfiles": "Profil kvality",
"Rating": "Hodnotenie",
"Search": "Hľadať",
"Seeders": "Seederi",
"Settings": "Nastavenia",
"Usenet": "Usenet",
"Custom": "Vlastné",
"CustomFormat": "Vlastný formát",
"Enable": "Povoliť",
"IMDb": "IMDb",
"Info": "Info",
"LocalPath": "Miestna cesta",
"Queued": "Fronta",
"Updates": "Aktualizovať",
"Indexers": "Indexery",
"BlocklistReleases": "Blocklistnúť vydanie",
"CustomFormats": "Vlastný formát",
"Cutoff": "Hranica",
"DeleteCustomFormat": "Klonovať vlastný formát",
"Disabled": "zakázané",
"Formats": "Formát",
"Host": "Hostiteľ",
"Hostname": "Názov hostiteľa",
"iCalLink": "iCal Link",
"List": "Zoznam",
"Lists": "Zoznam",
"Metadata": "metadáta",
"Movie": "Film",
"Quality": "kvalita",
"Queue": "Fronta",
"RecyclingBin": "Kôš",
"Refresh": "Obnoviť",
"RemotePathMappings": "Vzdialené mapovanie cesty",
"Replace": "Nahradiť",
"Runtime": "Beh programu",
"Scheduled": "Naplánované",
"SettingsRemotePathMappingLocalPath": "Miestna cesta",
"UpdateMechanismHelpText": "Použiť vstavaný Prowlarr aktualizátor alebo skript",
"URLBase": "Základ URL",
"Protocol": "Protokol",
"Files": "Súbor",
"Enabled": "Povoliť",
"Movies": "Film",
"New": "Nový",
"Ratings": "Hodnotenie",
"Restrictions": "Obmedzenie",
"RootFolder": "Koreňový priečinok",
"RootFolders": "Koreňový priečinok",
"RSS": "RSS",
"Title": "Názov",
"Titles": "Názov",
"Username": "Používateľské meno"
}

View File

@@ -508,13 +508,13 @@
"Add": "Lägg till",
"CertValidationNoLocal": "Inaktiverad för lokala adresser",
"BindAddress": "Bindningsadress",
"AreYouSureYouWantToRemoveSelectedItemsFromQueue": "Är du säker på vill ta bort {0} föremål{1} från kön?",
"AreYouSureYouWantToRemoveSelectedItemsFromQueue": "Är du säker på vill ta bort {0} föremål från kön?",
"Announced": "Utannonserad",
"Always": "Alltid",
"AddToDownloadQueue": "Tillagd till nedladdnings kö",
"AddToDownloadQueue": "Lägg till i nedladdningskö",
"AddRestriction": "Lägg till restriktion",
"AddImportExclusionHelpText": "Förhindra film att läggas till i Radarr från listor",
"AddedToDownloadQueue": "Lägg till i nedladdningskö",
"AddedToDownloadQueue": "Tillagd i nedladdningskö",
"Unreleased": "Inte tillgänglig",
"LinkHere": "här",
"Paused": "Pausad",
@@ -1066,5 +1066,8 @@
"Collections": "Samling",
"MonitorMovies": "Bevaka film",
"NoCollections": "Inga filmer hittades, för att komma igång lägg till en ny film eller importera några befintliga.",
"RssSyncHelpText": "Intervall i minuter. Ställ in på noll för att inaktivera (detta kommer stoppa all automatisk hämntning av utgåvor)"
"RssSyncHelpText": "Intervall i minuter. Ställ in på noll för att inaktivera (detta kommer stoppa all automatisk hämntning av utgåvor)",
"AnnouncedMsg": "Filmen tillkännages",
"ApplicationURL": "Applikations-URL",
"ApplicationUrlHelpText": "Denna applikations externa URL inklusive http(s)://, port och URL-bas"
}

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