mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2026-04-17 21:44:48 -04:00
Compare commits
174 Commits
v1.25.3.48
...
v1.34.0.50
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
075fd24f96 | ||
|
|
4ba72ea7f3 | ||
|
|
46f73c51bb | ||
|
|
3287d45661 | ||
|
|
71937fa44c | ||
|
|
6aefd46cd4 | ||
|
|
c8370c9e00 | ||
|
|
6be4203b41 | ||
|
|
1339373e43 | ||
|
|
fc9dfb0cf7 | ||
|
|
48301055ea | ||
|
|
8a9518c9c1 | ||
|
|
de099c6770 | ||
|
|
07711da4e0 | ||
|
|
7cb70716d0 | ||
|
|
548dedad5c | ||
|
|
7008626358 | ||
|
|
f6f2a3b00d | ||
|
|
2b16d93095 | ||
|
|
e63ee13d23 | ||
|
|
5c5a163151 | ||
|
|
023eec0ec0 | ||
|
|
5bc5f0e6b8 | ||
|
|
5cbacc01eb | ||
|
|
f4f1b38324 | ||
|
|
758dddd4ad | ||
|
|
73ee695633 | ||
|
|
27fbd7ef7e | ||
|
|
5125f256fb | ||
|
|
b99e8d0d65 | ||
|
|
d20b2cc9c0 | ||
|
|
8a1787bdb6 | ||
|
|
a19b8ea997 | ||
|
|
10ea6cd753 | ||
|
|
2c1b464715 | ||
|
|
3263454041 | ||
|
|
015db4a916 | ||
|
|
49268f3b8d | ||
|
|
f02a6f3e2c | ||
|
|
46b6124b97 | ||
|
|
53bc97b3be | ||
|
|
b09d4927cc | ||
|
|
328f3c0423 | ||
|
|
635e76526a | ||
|
|
790feed5ab | ||
|
|
59b5d2fc78 | ||
|
|
d5b12cf51a | ||
|
|
2d584f7eb6 | ||
|
|
0f1d647cd7 | ||
|
|
d6e8d89be4 | ||
|
|
8672129d5a | ||
|
|
44bdff8b8f | ||
|
|
4df8fc02f1 | ||
|
|
e101129cff | ||
|
|
147e732c9c | ||
|
|
a12381fb1d | ||
|
|
3a4de9cca1 | ||
|
|
43c988d951 | ||
|
|
a036e0fc37 | ||
|
|
56b9da16cf | ||
|
|
887c262589 | ||
|
|
12ff612775 | ||
|
|
0d3d27e46f | ||
|
|
d1846fde61 | ||
|
|
e6901506a0 | ||
|
|
08b4eddbc5 | ||
|
|
979db70e68 | ||
|
|
22834a852a | ||
|
|
f0540a5f8b | ||
|
|
1f7ac7d7d6 | ||
|
|
8ac68240ad | ||
|
|
b463a3f54b | ||
|
|
e15e57329e | ||
|
|
d8354408a4 | ||
|
|
6d2d49f7bd | ||
|
|
37610eec40 | ||
|
|
ed51208116 | ||
|
|
26e4dcad65 | ||
|
|
6eb21a02a1 | ||
|
|
8c2d5a404d | ||
|
|
3b83a00eaf | ||
|
|
a5a86a6f86 | ||
|
|
e7ed09a43d | ||
|
|
547bc2e58c | ||
|
|
8eb674c8d7 | ||
|
|
2c3621d25e | ||
|
|
2648f2c639 | ||
|
|
f4d621063b | ||
|
|
73494c462c | ||
|
|
36f6896f30 | ||
|
|
e01741a69e | ||
|
|
1dbff1235e | ||
|
|
1a9ad6b363 | ||
|
|
c88249300c | ||
|
|
7b8e352d87 | ||
|
|
81f7a6cbab | ||
|
|
523e46af2a | ||
|
|
2b4a6def2a | ||
|
|
9097c0ef6d | ||
|
|
4321c1d40c | ||
|
|
bb2548a08d | ||
|
|
3a9b841fad | ||
|
|
31203d1370 | ||
|
|
c8a910eaf4 | ||
|
|
9ab3c3e6c7 | ||
|
|
4659cb706a | ||
|
|
500759bf1f | ||
|
|
43c7c43257 | ||
|
|
9c2fced391 | ||
|
|
52ec5b6ff6 | ||
|
|
b46e657976 | ||
|
|
51fd30ba10 | ||
|
|
5fbb347108 | ||
|
|
54d3d44620 | ||
|
|
5ca18683ca | ||
|
|
6bdf5f5d69 | ||
|
|
7cba7152f1 | ||
|
|
cf012eb001 | ||
|
|
6b8a7993ff | ||
|
|
c6440bb21b | ||
|
|
b95eac98b9 | ||
|
|
0eb19ce834 | ||
|
|
4b8016d95d | ||
|
|
31d8d2419a | ||
|
|
d29ccd7749 | ||
|
|
e789f4ec54 | ||
|
|
58d495d618 | ||
|
|
f3328863e1 | ||
|
|
a23d792781 | ||
|
|
f066cf399d | ||
|
|
61e863cb31 | ||
|
|
b2afbc6872 | ||
|
|
aace65f88e | ||
|
|
9ab2d8b444 | ||
|
|
bc314061ef | ||
|
|
87b3dcd780 | ||
|
|
f3b99f68f6 | ||
|
|
c4a90e8ba4 | ||
|
|
41320ca2dc | ||
|
|
b8b32f8708 | ||
|
|
30c4bb24e8 | ||
|
|
b447db5d08 | ||
|
|
299001a513 | ||
|
|
2871f1f2a2 | ||
|
|
a9b93df0c9 | ||
|
|
2726787ee9 | ||
|
|
b917932f19 | ||
|
|
06ae85e6d1 | ||
|
|
b1c7e98664 | ||
|
|
62479737a7 | ||
|
|
8e69415d64 | ||
|
|
222dfb1821 | ||
|
|
94f439e238 | ||
|
|
903a88c121 | ||
|
|
9690ab6883 | ||
|
|
1e1a2b3b4a | ||
|
|
9dc2d3669c | ||
|
|
511c76e219 | ||
|
|
78329b7b92 | ||
|
|
4240048853 | ||
|
|
432af42ffd | ||
|
|
0d6c03f8d4 | ||
|
|
96830f975e | ||
|
|
13c538ff58 | ||
|
|
14250e9634 | ||
|
|
e2f7890d76 | ||
|
|
257d38de66 | ||
|
|
fd2a14e01b | ||
|
|
b4d76c7138 | ||
|
|
9655f37fa8 | ||
|
|
246fb9b855 | ||
|
|
25afadc9b2 | ||
|
|
3f547f0856 | ||
|
|
11e322b6d7 |
@@ -1,33 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="70px" height="70px" viewBox="0 0 70 70" style="enable-background:new 0 0 70 70;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-1.3318" y1="43.7371" x2="67.0419" y2="26.0967">
|
||||
<stop offset="0.1237" style="stop-color:#7866FF"/>
|
||||
<stop offset="0.5376" style="stop-color:#FE2EB6"/>
|
||||
<stop offset="0.8548" style="stop-color:#FD0486"/>
|
||||
</linearGradient>
|
||||
<polygon style="fill:url(#SVGID_1_);" points="67.3,16 43.7,0 0,31.1 11.1,70 58.9,60.3 "/>
|
||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="45.9148" y1="38.9098" x2="67.6577" y2="9.0989">
|
||||
<stop offset="0.1237" style="stop-color:#FF0080"/>
|
||||
<stop offset="0.2587" style="stop-color:#FE0385"/>
|
||||
<stop offset="0.4109" style="stop-color:#FA0C92"/>
|
||||
<stop offset="0.5713" style="stop-color:#F41BA9"/>
|
||||
<stop offset="0.7363" style="stop-color:#EB2FC8"/>
|
||||
<stop offset="0.8656" style="stop-color:#E343E6"/>
|
||||
</linearGradient>
|
||||
<polygon style="fill:url(#SVGID_2_);" points="67.3,16 43.7,0 38,15.7 38,47.8 70,47.8 "/>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="13.4" y="13.4" style="fill:#000000;" width="43.2" height="43.2"/>
|
||||
<rect x="17.4" y="48.5" style="fill:#FFFFFF;" width="16.2" height="2.7"/>
|
||||
<g>
|
||||
<path style="fill:#FFFFFF;" d="M17.4,19.1h6.9c5.6,0,9.5,3.8,9.5,8.9V28c0,5-3.9,8.9-9.5,8.9h-6.9V19.1z M21.4,22.7v10.7h3
|
||||
c3.2,0,5.4-2.2,5.4-5.3V28c0-3.2-2.2-5.4-5.4-5.4H21.4z"/>
|
||||
<polygon style="fill:#FFFFFF;" points="40.3,22.7 34.9,22.7 34.9,19.1 49.6,19.1 49.6,22.7 44.2,22.7 44.2,37 40.3,37 "/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1,66 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="120.1px" height="130.2px" viewBox="0 0 120.1 130.2" style="enable-background:new 0 0 120.1 130.2;" xml:space="preserve"
|
||||
>
|
||||
<g>
|
||||
<linearGradient id="XMLID_2_" gradientUnits="userSpaceOnUse" x1="31.8412" y1="120.5578" x2="110.2402" y2="73.24">
|
||||
<stop offset="0" style="stop-color:#FCEE39"/>
|
||||
<stop offset="1" style="stop-color:#F37B3D"/>
|
||||
</linearGradient>
|
||||
<path id="XMLID_3041_" style="fill:url(#XMLID_2_);" d="M118.6,71.8c0.9-0.8,1.4-1.9,1.5-3.2c0.1-2.6-1.8-4.7-4.4-4.9
|
||||
c-1.2-0.1-2.4,0.4-3.3,1.1l0,0l-83.8,45.9c-1.9,0.8-3.6,2.2-4.7,4.1c-2.9,4.8-1.3,11,3.6,13.9c3.4,2,7.5,1.8,10.7-0.2l0,0l0,0
|
||||
c0.2-0.2,0.5-0.3,0.7-0.5l78-54.8C117.3,72.9,118.4,72.1,118.6,71.8L118.6,71.8L118.6,71.8z"/>
|
||||
<linearGradient id="XMLID_3_" gradientUnits="userSpaceOnUse" x1="48.3607" y1="6.9083" x2="119.9179" y2="69.5546">
|
||||
<stop offset="0" style="stop-color:#EF5A6B"/>
|
||||
<stop offset="0.57" style="stop-color:#F26F4E"/>
|
||||
<stop offset="1" style="stop-color:#F37B3D"/>
|
||||
</linearGradient>
|
||||
<path id="XMLID_3049_" style="fill:url(#XMLID_3_);" d="M118.8,65.1L118.8,65.1L55,2.5C53.6,1,51.6,0,49.3,0
|
||||
c-4.3,0-7.7,3.5-7.7,7.7v0c0,2.1,0.8,3.9,2.1,5.3l0,0l0,0c0.4,0.4,0.8,0.7,1.2,1l67.4,57.7l0,0c0.8,0.7,1.8,1.2,3,1.3
|
||||
c2.6,0.1,4.7-1.8,4.9-4.4C120.2,67.3,119.7,66,118.8,65.1z"/>
|
||||
<linearGradient id="XMLID_4_" gradientUnits="userSpaceOnUse" x1="52.9467" y1="63.6407" x2="10.5379" y2="37.1562">
|
||||
<stop offset="0" style="stop-color:#7C59A4"/>
|
||||
<stop offset="0.3852" style="stop-color:#AF4C92"/>
|
||||
<stop offset="0.7654" style="stop-color:#DC4183"/>
|
||||
<stop offset="0.957" style="stop-color:#ED3D7D"/>
|
||||
</linearGradient>
|
||||
<path id="XMLID_3042_" style="fill:url(#XMLID_4_);" d="M57.1,59.5C57,59.5,17.7,28.5,16.9,28l0,0l0,0c-0.6-0.3-1.2-0.6-1.8-0.9
|
||||
c-5.8-2.2-12.2,0.8-14.4,6.6c-1.9,5.1,0.2,10.7,4.6,13.4l0,0l0,0C6,47.5,6.6,47.8,7.3,48c0.4,0.2,45.4,18.8,45.4,18.8l0,0
|
||||
c1.8,0.8,3.9,0.3,5.1-1.2C59.3,63.7,59,61,57.1,59.5z"/>
|
||||
<linearGradient id="XMLID_5_" gradientUnits="userSpaceOnUse" x1="52.1736" y1="3.7019" x2="10.7706" y2="37.8971">
|
||||
<stop offset="0" style="stop-color:#EF5A6B"/>
|
||||
<stop offset="0.364" style="stop-color:#EE4E72"/>
|
||||
<stop offset="1" style="stop-color:#ED3D7D"/>
|
||||
</linearGradient>
|
||||
<path id="XMLID_3057_" style="fill:url(#XMLID_5_);" d="M49.3,0c-1.7,0-3.3,0.6-4.6,1.5L4.9,28.3c-0.1,0.1-0.2,0.1-0.2,0.2l-0.1,0
|
||||
l0,0c-1.7,1.2-3.1,3-3.9,5.1C-1.5,39.4,1.5,45.9,7.3,48c3.6,1.4,7.5,0.7,10.4-1.4l0,0l0,0c0.7-0.5,1.3-1,1.8-1.6l34.6-31.2l0,0
|
||||
c1.8-1.4,3-3.6,3-6.1v0C57.1,3.5,53.6,0,49.3,0z"/>
|
||||
<g id="XMLID_3008_">
|
||||
<rect id="XMLID_3033_" x="34.6" y="37.4" style="fill:#000000;" width="51" height="51"/>
|
||||
<rect id="XMLID_3032_" x="39" y="78.8" style="fill:#FFFFFF;" width="19.1" height="3.2"/>
|
||||
<g id="XMLID_3009_">
|
||||
<path id="XMLID_3030_" style="fill:#FFFFFF;" d="M38.8,50.8l1.5-1.4c0.4,0.5,0.8,0.8,1.3,0.8c0.6,0,0.9-0.4,0.9-1.2l0-5.3l2.3,0
|
||||
l0,5.3c0,1-0.3,1.8-0.8,2.3c-0.5,0.5-1.3,0.8-2.3,0.8C40.2,52.2,39.4,51.6,38.8,50.8z"/>
|
||||
<path id="XMLID_3028_" style="fill:#FFFFFF;" d="M45.3,43.8l6.7,0v1.9l-4.4,0V47l4,0l0,1.8l-4,0l0,1.3l4.5,0l0,2l-6.7,0
|
||||
L45.3,43.8z"/>
|
||||
<path id="XMLID_3026_" style="fill:#FFFFFF;" d="M55,45.8l-2.5,0l0-2l7.3,0l0,2l-2.5,0l0,6.3l-2.3,0L55,45.8z"/>
|
||||
<path id="XMLID_3022_" style="fill:#FFFFFF;" d="M39,54l4.3,0c1,0,1.8,0.3,2.3,0.7c0.3,0.3,0.5,0.8,0.5,1.4v0
|
||||
c0,1-0.5,1.5-1.3,1.9c1,0.3,1.6,0.9,1.6,2v0c0,1.4-1.2,2.3-3.1,2.3l-4.3,0L39,54z M43.8,56.6c0-0.5-0.4-0.7-1-0.7l-1.5,0l0,1.5
|
||||
l1.4,0C43.4,57.3,43.8,57.1,43.8,56.6L43.8,56.6z M43,59l-1.8,0l0,1.5H43c0.7,0,1.1-0.3,1.1-0.8v0C44.1,59.2,43.7,59,43,59z"/>
|
||||
<path id="XMLID_3019_" style="fill:#FFFFFF;" d="M46.8,54l3.9,0c1.3,0,2.1,0.3,2.7,0.9c0.5,0.5,0.7,1.1,0.7,1.9v0
|
||||
c0,1.3-0.7,2.1-1.7,2.6l2,2.9l-2.6,0l-1.7-2.5h-1l0,2.5l-2.3,0L46.8,54z M50.6,58c0.8,0,1.2-0.4,1.2-1v0c0-0.7-0.5-1-1.2-1
|
||||
l-1.5,0v2H50.6z"/>
|
||||
<path id="XMLID_3016_" style="fill:#FFFFFF;" d="M56.8,54l2.2,0l3.5,8.4l-2.5,0l-0.6-1.5l-3.2,0l-0.6,1.5l-2.4,0L56.8,54z
|
||||
M58.8,59l-0.9-2.3L57,59L58.8,59z"/>
|
||||
<path id="XMLID_3014_" style="fill:#FFFFFF;" d="M62.8,54l2.3,0l0,8.3l-2.3,0L62.8,54z"/>
|
||||
<path id="XMLID_3012_" style="fill:#FFFFFF;" d="M65.7,54l2.1,0l3.4,4.4l0-4.4l2.3,0l0,8.3l-2,0L68,57.8l0,4.6l-2.3,0L65.7,54z"
|
||||
/>
|
||||
<path id="XMLID_3010_" style="fill:#FFFFFF;" d="M73.7,61.1l1.3-1.5c0.8,0.7,1.7,1,2.7,1c0.6,0,1-0.2,1-0.6v0
|
||||
c0-0.4-0.3-0.5-1.4-0.8c-1.8-0.4-3.1-0.9-3.1-2.6v0c0-1.5,1.2-2.7,3.2-2.7c1.4,0,2.5,0.4,3.4,1.1l-1.2,1.6
|
||||
c-0.8-0.5-1.6-0.8-2.3-0.8c-0.6,0-0.8,0.2-0.8,0.5v0c0,0.4,0.3,0.5,1.4,0.8c1.9,0.4,3.1,1,3.1,2.6v0c0,1.7-1.3,2.7-3.4,2.7
|
||||
C76.1,62.5,74.7,62,73.7,61.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.8 KiB |
@@ -1,50 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="70px" height="70px" viewBox="0 0 70 70" style="enable-background:new 0 0 70 70;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="22.9451" y1="75.7869" x2="74.7868" y2="20.6415">
|
||||
<stop offset="1.612903e-002" style="stop-color:#B35BA3"/>
|
||||
<stop offset="0.4044" style="stop-color:#C41E57"/>
|
||||
<stop offset="0.4677" style="stop-color:#C41E57"/>
|
||||
<stop offset="0.6505" style="stop-color:#EB8523"/>
|
||||
<stop offset="0.9516" style="stop-color:#FEBD11"/>
|
||||
</linearGradient>
|
||||
<polygon style="fill:url(#SVGID_1_);" points="49.8,15.2 36,36.7 58.4,70 70,23.1 "/>
|
||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="17.7187" y1="73.2922" x2="69.5556" y2="18.1519">
|
||||
<stop offset="1.612903e-002" style="stop-color:#B35BA3"/>
|
||||
<stop offset="0.4044" style="stop-color:#C41E57"/>
|
||||
<stop offset="0.4677" style="stop-color:#C41E57"/>
|
||||
<stop offset="0.7043" style="stop-color:#EB8523"/>
|
||||
</linearGradient>
|
||||
<polygon style="fill:url(#SVGID_2_);" points="51.1,15.7 49,0 18.8,33.6 27.6,42.3 20.8,70 58.4,70 "/>
|
||||
</g>
|
||||
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="1.8281" y1="53.4275" x2="48.8245" y2="9.2255">
|
||||
<stop offset="1.612903e-002" style="stop-color:#B35BA3"/>
|
||||
<stop offset="0.6613" style="stop-color:#C41E57"/>
|
||||
</linearGradient>
|
||||
<polygon style="fill:url(#SVGID_3_);" points="49,0 11.6,0 0,47.1 55.6,47.1 "/>
|
||||
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="49.8935" y1="-11.5569" x2="48.8588" y2="24.0352">
|
||||
<stop offset="0.5" style="stop-color:#C41E57"/>
|
||||
<stop offset="0.6668" style="stop-color:#D13F48"/>
|
||||
<stop offset="0.7952" style="stop-color:#D94F39"/>
|
||||
<stop offset="0.8656" style="stop-color:#DD5433"/>
|
||||
</linearGradient>
|
||||
<polygon style="fill:url(#SVGID_4_);" points="55.3,47.1 51.1,15.7 49,0 41.7,23 "/>
|
||||
</g>
|
||||
<g>
|
||||
|
||||
<rect x="13.4" y="13.5" transform="matrix(-1 2.577289e-003 -2.577289e-003 -1 70.0288 70.081)" style="fill:#000000;" width="43.2" height="43.2"/>
|
||||
|
||||
<rect x="17.6" y="48.6" transform="matrix(1 -2.577289e-003 2.577289e-003 1 -0.1287 6.634109e-002)" style="fill:#FFFFFF;" width="16.2" height="2.7"/>
|
||||
<path style="fill:#FFFFFF;" d="M17.4,19.1l8.2,0c2.3,0,4,0.6,5.2,1.8c1,1,1.5,2.4,1.5,4.1l0,0.1c0,1.5-0.3,2.6-1.1,3.5
|
||||
c-0.7,0.9-1.6,1.6-2.8,2l4.4,6.4l-4.6,0l-3.7-5.5l-3.3,0l0,5.5l-3.9,0L17.4,19.1z M25.3,27.8c1,0,1.7-0.2,2.2-0.7
|
||||
c0.5-0.5,0.8-1.1,0.8-1.8l0-0.1c0-0.9-0.3-1.5-0.8-1.9c-0.5-0.4-1.3-0.6-2.3-0.6l-3.9,0l0,5.1L25.3,27.8z"/>
|
||||
<path style="fill:#FFFFFF;" d="M36,33.2l-1.9,0l0-3.3l2.5,0l0.6-3.8l-2.3,0l0-3.3l2.8,0l0.6-3.7l3.4,0l-0.6,3.7l3.7,0l0.6-3.7
|
||||
l3.4,0l-0.6,3.7l1.9,0l0,3.3l-2.5,0L47,29.9l2.3,0l0,3.3l-2.8,0L45.8,37l-3.4,0l0.7-3.8l-3.7,0L38.7,37l-3.4,0L36,33.2z
|
||||
M43.7,29.9l0.6-3.8l-3.7,0L40,29.9L43.7,29.9z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="70px" height="70px" viewBox="0 0 70 70" style="enable-background:new 0 0 70 70;" xml:space="preserve">
|
||||
<defs>
|
||||
<linearGradient id="linear-gradient" x1="70.22612" y1="27.79912" x2="-5.13024" y2="63.12242" gradientTransform="matrix(1, 0, 0, -1, 0, 71.27997)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#c90f5e"/>
|
||||
<stop offset="0.22111" stop-color="#c90f5e"/>
|
||||
<stop offset="0.2356" stop-color="#c90f5e"/>
|
||||
<stop offset="0.35559" stop-color="#ca135c"/>
|
||||
<stop offset="0.46633" stop-color="#ce1e57"/>
|
||||
<stop offset="0.5735" stop-color="#d4314e"/>
|
||||
<stop offset="0.67844" stop-color="#dc4b41"/>
|
||||
<stop offset="0.78179" stop-color="#e66d31"/>
|
||||
<stop offset="0.88253" stop-color="#f3961d"/>
|
||||
<stop offset="0.94241" stop-color="#fcb20f"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linear-gradient-2" x1="24.65904" y1="61.99608" x2="46.04762" y2="2.93445" gradientTransform="matrix(1, 0, 0, -1, 0, 71.27997)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.04188" stop-color="#077cfb"/>
|
||||
<stop offset="0.44503" stop-color="#c90f5e"/>
|
||||
<stop offset="0.95812" stop-color="#077cfb"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linear-gradient-3" x1="17.39552" y1="63.34592" x2="33.19389" y2="7.20092" gradientTransform="matrix(1, 0, 0, -1, 0, 71.27997)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.27749" stop-color="#c90f5e"/>
|
||||
<stop offset="0.97382" stop-color="#fcb20f"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<title>rider</title>
|
||||
<g>
|
||||
<polygon points="70 27.237 63.391 23.75 20.926 0 3.827 17.921 21.619 41.068 60.537 44.397 70 27.237" fill="url(#linear-gradient)"/>
|
||||
<polygon points="50.423 16.132 44.271 1.107 27.643 17.471 11.768 50.194 49.411 70 70 57.98 50.423 16.132" fill="url(#linear-gradient-2)"/>
|
||||
<polygon points="20.926 0 0 14.095 7.779 62.172 27.848 69.889 53.78 48.823 20.926 0" fill="url(#linear-gradient-3)"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="13.30219" y="13.19311" width="43.61371" height="43.61371"/>
|
||||
<g>
|
||||
<path d="M17.22741,18.86293h8.39564a7.38416,7.38416,0,0,1,5.34268,1.85358,5.86989,5.86989,0,0,1,1.52648,4.1433h0A5.74339,5.74339,0,0,1,28.567,30.5296l4.47041,6.54206H28.34891L24.42368,31.1838h-3.162v5.88785H17.22741V18.86293h0ZM25.296,27.69471c1.96262,0,3.053-1.09034,3.053-2.61682h0c0-1.74455-1.19938-2.61682-3.162-2.61682H21.15265v5.23365H25.296Z" fill="#fff"/>
|
||||
<path d="M36.09034,18.86293H43.2866c5.77882,0,9.70405,3.92523,9.70405,9.15888h0c0,5.12461-3.92523,9.15888-9.70405,9.15888H36.09034V18.86293Zm4.03427,3.59813V33.47352h3.162a5.23727,5.23727,0,0,0,5.56075-5.45171h0a5.26493,5.26493,0,0,0-5.56075-5.56075h-3.162Z" fill="#fff"/>
|
||||
</g>
|
||||
<rect x="17.22741" y="48.62925" width="16.35514" height="2.72586" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.0 KiB |
@@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="70px" height="70px" viewBox="0 0 70 70" style="enable-background:new 0 0 70 70;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="25.0676" y1="1.4599" x2="43.1829" y2="66.675">
|
||||
<stop offset="0.2849" style="stop-color:#00CDD7"/>
|
||||
<stop offset="0.9409" style="stop-color:#2086D7"/>
|
||||
</linearGradient>
|
||||
<polygon style="fill:url(#SVGID_1_);" points="9.4,63.3 0,7.3 17.5,0.1 28.6,6.7 38.8,1.2 60.1,9.4 48.1,70 "/>
|
||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="30.7199" y1="9.7343" x2="61.365" y2="54.6713">
|
||||
<stop offset="0.1398" style="stop-color:#FFF045"/>
|
||||
<stop offset="0.3656" style="stop-color:#00CDD7"/>
|
||||
</linearGradient>
|
||||
<polygon style="fill:url(#SVGID_2_);" points="70,23.7 61,1.4 44.6,0 19.3,24.3 26.1,55.6 38.8,64.6 70,46 62.3,31.7 "/>
|
||||
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="61.0819" y1="15.2899" x2="65.1065" y2="29.5436">
|
||||
<stop offset="0.2849" style="stop-color:#00CDD7"/>
|
||||
<stop offset="0.9409" style="stop-color:#2086D7"/>
|
||||
</linearGradient>
|
||||
<polygon style="fill:url(#SVGID_3_);" points="56,20.4 62.3,31.7 70,23.7 64.4,9.8 "/>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="13.4" y="13.4" style="fill:#000000;" width="43.2" height="43.2"/>
|
||||
<rect x="17.5" y="48.5" style="fill:#FFFFFF;" width="16.2" height="2.7"/>
|
||||
<path style="fill:#FFFFFF;" d="M38.7,34.3l2.3-2.8c1.6,1.3,3.3,2.2,5.3,2.2c1.6,0,2.5-0.6,2.5-1.7v-0.1c0-1-0.6-1.5-3.6-2.3
|
||||
c-3.6-0.9-5.8-1.9-5.8-5.5v-0.1c0-3.3,2.6-5.4,6.2-5.4c2.6,0,4.8,0.8,6.6,2.3l-2,3c-1.6-1.1-3.1-1.8-4.6-1.8
|
||||
c-1.5,0-2.3,0.7-2.3,1.6v0.1c0,1.2,0.8,1.6,3.8,2.4c3.6,1,5.6,2.3,5.6,5.4v0.1c0,3.6-2.7,5.6-6.5,5.6
|
||||
C43.5,37.2,40.8,36.2,38.7,34.3"/>
|
||||
</g>
|
||||
<polygon style="fill:#FFFFFF;" points="35.2,19 32.5,29.4 29.5,19 26.5,19 23.4,29.4 20.7,19 16.6,19 21.7,36.9 25,36.9 28,26.5
|
||||
30.9,36.9 34.3,36.9 39.4,19 "/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
12
README.md
12
README.md
@@ -68,16 +68,16 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
|
||||
|
||||
## JetBrains
|
||||
|
||||
Thank you to [<img src="/Logo/jetbrains.svg" alt="JetBrains" width="32"> JetBrains](http://www.jetbrains.com/) for providing us with free licenses to their great tools.
|
||||
Thank you to [<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.png" alt="JetBrains" width="96">](http://www.jetbrains.com/) for providing us with free licenses to their great tools.
|
||||
|
||||
- [<img src="/Logo/resharper.svg" alt="ReSharper" width="32"> ReSharper](http://www.jetbrains.com/resharper/)
|
||||
- [<img src="/Logo/webstorm.svg" alt="WebStorm" width="32"> WebStorm](http://www.jetbrains.com/webstorm/)
|
||||
- [<img src="/Logo/rider.svg" alt="Rider" width="32"> Rider](http://www.jetbrains.com/rider/)
|
||||
- [<img src="/Logo/dottrace.svg" alt="dotTrace" width="32"> dotTrace](http://www.jetbrains.com/dottrace/)
|
||||
* [<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/ReSharper_icon.png" alt="ReSharper" width="32"> ReSharper](http://www.jetbrains.com/resharper/)
|
||||
* [<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/WebStorm_icon.png" alt="WebStorm" width="32"> WebStorm](http://www.jetbrains.com/webstorm/)
|
||||
* [<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/Rider_icon.png" alt="Rider" width="32"> Rider](http://www.jetbrains.com/rider/)
|
||||
* [<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/dotTrace_icon.png" alt="dotTrace" width="32"> dotTrace](http://www.jetbrains.com/dottrace/)
|
||||
|
||||
### License
|
||||
|
||||
- [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
|
||||
- Copyright 2010-2022
|
||||
- Copyright 2010-2024
|
||||
|
||||
Icon Credit - [Box vector created by freepik - www.freepik.com](https://www.freepik.com/vectors/box)
|
||||
|
||||
@@ -9,7 +9,7 @@ variables:
|
||||
testsFolder: './_tests'
|
||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||
majorVersion: '1.25.3'
|
||||
majorVersion: '1.34.0'
|
||||
minorVersion: $[counter('minorVersion', 1)]
|
||||
prowlarrVersion: '$(majorVersion).$(minorVersion)'
|
||||
buildName: '$(Build.SourceBranchName).$(prowlarrVersion)'
|
||||
@@ -19,7 +19,7 @@ variables:
|
||||
nodeVersion: '20.X'
|
||||
innoVersion: '6.2.2'
|
||||
windowsImage: 'windows-2022'
|
||||
linuxImage: 'ubuntu-20.04'
|
||||
linuxImage: 'ubuntu-22.04'
|
||||
macImage: 'macOS-13'
|
||||
|
||||
trigger:
|
||||
@@ -1169,12 +1169,12 @@ stages:
|
||||
submodules: true
|
||||
- powershell: Set-Service SCardSvr -StartupType Manual
|
||||
displayName: Enable Windows Test Service
|
||||
- task: SonarCloudPrepare@2
|
||||
- task: SonarCloudPrepare@3
|
||||
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
||||
inputs:
|
||||
SonarCloud: 'SonarCloud'
|
||||
organization: 'prowlarr'
|
||||
scannerMode: 'MSBuild'
|
||||
scannerMode: 'dotnet'
|
||||
projectKey: 'Prowlarr_Prowlarr'
|
||||
projectName: 'Prowlarr'
|
||||
projectVersion: '$(prowlarrVersion)'
|
||||
@@ -1187,10 +1187,10 @@ stages:
|
||||
./build.sh --backend -f net6.0 -r win-x64
|
||||
TEST_DIR=_tests/net6.0/win-x64/publish/ ./test.sh Windows Unit Coverage
|
||||
displayName: Coverage Unit Tests
|
||||
- task: SonarCloudAnalyze@2
|
||||
- task: SonarCloudAnalyze@3
|
||||
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
||||
displayName: Publish SonarCloud Results
|
||||
- task: reportgenerator@5
|
||||
- task: reportgenerator@5.3.11
|
||||
displayName: Generate Coverage Report
|
||||
inputs:
|
||||
reports: '$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml'
|
||||
|
||||
11
docs.sh
11
docs.sh
@@ -3,15 +3,16 @@ set -e
|
||||
|
||||
FRAMEWORK="net6.0"
|
||||
PLATFORM=$1
|
||||
ARCHITECTURE="${2:-x64}"
|
||||
|
||||
if [ "$PLATFORM" = "Windows" ]; then
|
||||
RUNTIME="win-x64"
|
||||
RUNTIME="win-$ARCHITECTURE"
|
||||
elif [ "$PLATFORM" = "Linux" ]; then
|
||||
RUNTIME="linux-x64"
|
||||
RUNTIME="linux-$ARCHITECTURE"
|
||||
elif [ "$PLATFORM" = "Mac" ]; then
|
||||
RUNTIME="osx-x64"
|
||||
RUNTIME="osx-$ARCHITECTURE"
|
||||
else
|
||||
echo "Platform must be provided as first arguement: Windows, Linux or Mac"
|
||||
echo "Platform must be provided as first argument: Windows, Linux or Mac"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -37,7 +38,7 @@ dotnet clean $slnFile -c Release
|
||||
dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids
|
||||
|
||||
dotnet new tool-manifest
|
||||
dotnet tool install --version 6.6.2 Swashbuckle.AspNetCore.Cli
|
||||
dotnet tool install --version 7.3.2 Swashbuckle.AspNetCore.Cli
|
||||
|
||||
dotnet tool run swagger tofile --output ./src/Prowlarr.Api.V1/openapi.json "$outputFolder/$FRAMEWORK/$RUNTIME/$application" v1 &
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ module.exports = (env) => {
|
||||
const config = {
|
||||
mode: isProduction ? 'production' : 'development',
|
||||
devtool: isProduction ? 'source-map' : 'eval-source-map',
|
||||
target: 'web',
|
||||
|
||||
stats: {
|
||||
children: false
|
||||
@@ -169,7 +170,7 @@ module.exports = (env) => {
|
||||
loose: true,
|
||||
debug: false,
|
||||
useBuiltIns: 'entry',
|
||||
corejs: 3
|
||||
corejs: '3.39'
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -20,6 +20,8 @@ import HintedSelectInputSelectedValue from './HintedSelectInputSelectedValue';
|
||||
import TextInput from './TextInput';
|
||||
import styles from './EnhancedSelectInput.css';
|
||||
|
||||
const MINIMUM_DISTANCE_FROM_EDGE = 10;
|
||||
|
||||
function isArrowKey(keyCode) {
|
||||
return keyCode === keyCodes.UP_ARROW || keyCode === keyCodes.DOWN_ARROW;
|
||||
}
|
||||
@@ -137,18 +139,9 @@ class EnhancedSelectInput extends Component {
|
||||
// Listeners
|
||||
|
||||
onComputeMaxHeight = (data) => {
|
||||
const {
|
||||
top,
|
||||
bottom
|
||||
} = data.offsets.reference;
|
||||
|
||||
const windowHeight = window.innerHeight;
|
||||
|
||||
if ((/^botton/).test(data.placement)) {
|
||||
data.styles.maxHeight = windowHeight - bottom;
|
||||
} else {
|
||||
data.styles.maxHeight = top;
|
||||
}
|
||||
data.styles.maxHeight = windowHeight - MINIMUM_DISTANCE_FROM_EDGE;
|
||||
|
||||
return data;
|
||||
};
|
||||
@@ -460,6 +453,10 @@ class EnhancedSelectInput extends Component {
|
||||
order: 851,
|
||||
enabled: true,
|
||||
fn: this.onComputeMaxHeight
|
||||
},
|
||||
preventOverflow: {
|
||||
enabled: true,
|
||||
boundariesElement: 'viewport'
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
composes: link;
|
||||
|
||||
padding: 10px 24px;
|
||||
padding-left: 35px;
|
||||
}
|
||||
|
||||
.isActiveLink {
|
||||
@@ -41,10 +42,6 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.noIcon {
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
.status {
|
||||
float: right;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ interface CssExports {
|
||||
'isActiveParentLink': string;
|
||||
'item': string;
|
||||
'link': string;
|
||||
'noIcon': string;
|
||||
'status': string;
|
||||
}
|
||||
export const cssExports: CssExports;
|
||||
|
||||
@@ -63,9 +63,7 @@ class PageSidebarItem extends Component {
|
||||
</span>
|
||||
}
|
||||
|
||||
<span className={isChildItem ? styles.noIcon : null}>
|
||||
{typeof title === 'function' ? title() : title}
|
||||
</span>
|
||||
{typeof title === 'function' ? title() : title}
|
||||
|
||||
{
|
||||
!!StatusComponent &&
|
||||
|
||||
@@ -22,11 +22,14 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.label {
|
||||
padding: 0 3px;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
color: var(--toolbarLabelColor);
|
||||
font-size: $extraSmallFontSize;
|
||||
line-height: calc($extraSmallFontSize + 1px);
|
||||
|
||||
@@ -23,6 +23,7 @@ function PageToolbarButton(props) {
|
||||
isDisabled && styles.isDisabled
|
||||
)}
|
||||
isDisabled={isDisabled || isSpinning}
|
||||
title={label}
|
||||
{...otherProps}
|
||||
>
|
||||
<Icon
|
||||
|
||||
@@ -257,6 +257,7 @@ class HistoryRow extends Component {
|
||||
key={parameter.key}
|
||||
title={parameter.title}
|
||||
value={data[parameter.key]}
|
||||
queryType={data.queryType}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import React from 'react';
|
||||
import Link from 'Components/Link/Link';
|
||||
import { HistoryQueryType } from 'typings/History';
|
||||
import styles from './HistoryRowParameter.css';
|
||||
|
||||
interface HistoryRowParameterProps {
|
||||
title: string;
|
||||
value: string;
|
||||
queryType: HistoryQueryType;
|
||||
}
|
||||
|
||||
function HistoryRowParameter(props: HistoryRowParameterProps) {
|
||||
const { title, value } = props;
|
||||
const { title, value, queryType } = props;
|
||||
|
||||
const type = title.toLowerCase();
|
||||
|
||||
@@ -18,7 +20,13 @@ function HistoryRowParameter(props: HistoryRowParameterProps) {
|
||||
link = <Link to={`https://imdb.com/title/${value}/`}>{value}</Link>;
|
||||
} else if (type === 'tmdb') {
|
||||
link = (
|
||||
<Link to={`https://www.themoviedb.org/movie/${value}`}>{value}</Link>
|
||||
<Link
|
||||
to={`https://www.themoviedb.org/${
|
||||
queryType === 'tvsearch' ? 'tv' : 'movie'
|
||||
}/${value}`}
|
||||
>
|
||||
{value}
|
||||
</Link>
|
||||
);
|
||||
} else if (type === 'tvdb') {
|
||||
link = (
|
||||
|
||||
@@ -68,6 +68,7 @@ function IndexerHistoryRow(props: IndexerHistoryRowProps) {
|
||||
key={parameter.key}
|
||||
title={parameter.title}
|
||||
value={data[parameter.key as keyof HistoryData].toString()}
|
||||
queryType={data.queryType}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -270,7 +270,7 @@ function Updates() {
|
||||
|
||||
{generalSettingsError ? (
|
||||
<Alert kind={kinds.DANGER}>
|
||||
{translate('FailedToUpdateSettings')}
|
||||
{translate('FailedToFetchSettings')}
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export async function fetchTranslations(): Promise<boolean> {
|
||||
translations = data.Strings;
|
||||
|
||||
resolve(true);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import ModelBase from 'App/ModelBase';
|
||||
|
||||
export type HistoryQueryType =
|
||||
| 'search'
|
||||
| 'tvsearch'
|
||||
| 'movie'
|
||||
| 'book'
|
||||
| 'music';
|
||||
|
||||
export interface HistoryData {
|
||||
source: string;
|
||||
host: string;
|
||||
@@ -7,7 +14,7 @@ export interface HistoryData {
|
||||
offset: number;
|
||||
elapsedTime: number;
|
||||
query: string;
|
||||
queryType: string;
|
||||
queryType: HistoryQueryType;
|
||||
}
|
||||
|
||||
interface History extends ModelBase {
|
||||
|
||||
28
package.json
28
package.json
@@ -23,10 +23,10 @@
|
||||
"defaults"
|
||||
],
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "6.6.0",
|
||||
"@fortawesome/fontawesome-svg-core": "6.6.0",
|
||||
"@fortawesome/free-regular-svg-icons": "6.6.0",
|
||||
"@fortawesome/free-solid-svg-icons": "6.6.0",
|
||||
"@fortawesome/fontawesome-free": "6.7.1",
|
||||
"@fortawesome/fontawesome-svg-core": "6.7.1",
|
||||
"@fortawesome/free-regular-svg-icons": "6.7.1",
|
||||
"@fortawesome/free-solid-svg-icons": "6.7.1",
|
||||
"@fortawesome/react-fontawesome": "0.2.2",
|
||||
"@juggle/resize-observer": "3.4.0",
|
||||
"@microsoft/signalr": "6.0.25",
|
||||
@@ -81,30 +81,30 @@
|
||||
"redux-thunk": "2.4.2",
|
||||
"reselect": "4.1.8",
|
||||
"stacktrace-js": "2.0.2",
|
||||
"typescript": "5.1.6"
|
||||
"typescript": "5.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.25.8",
|
||||
"@babel/eslint-parser": "7.25.8",
|
||||
"@babel/plugin-proposal-export-default-from": "7.25.8",
|
||||
"@babel/core": "7.26.0",
|
||||
"@babel/eslint-parser": "7.25.9",
|
||||
"@babel/plugin-proposal-export-default-from": "7.25.9",
|
||||
"@babel/plugin-syntax-dynamic-import": "7.8.3",
|
||||
"@babel/preset-env": "7.25.8",
|
||||
"@babel/preset-react": "7.25.7",
|
||||
"@babel/preset-typescript": "7.25.7",
|
||||
"@babel/preset-env": "7.26.0",
|
||||
"@babel/preset-react": "7.26.3",
|
||||
"@babel/preset-typescript": "7.26.0",
|
||||
"@types/lodash": "4.14.195",
|
||||
"@types/react-document-title": "2.0.10",
|
||||
"@types/react-router-dom": "5.3.3",
|
||||
"@types/react-text-truncate": "0.19.0",
|
||||
"@types/react-window": "1.8.8",
|
||||
"@types/webpack-livereload-plugin": "2.3.6",
|
||||
"@typescript-eslint/eslint-plugin": "6.21.0",
|
||||
"@typescript-eslint/parser": "6.21.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.18.1",
|
||||
"@typescript-eslint/parser": "8.18.1",
|
||||
"are-you-es5": "2.1.2",
|
||||
"autoprefixer": "10.4.20",
|
||||
"babel-loader": "9.2.1",
|
||||
"babel-plugin-inline-classnames": "2.0.1",
|
||||
"babel-plugin-transform-react-remove-prop-types": "0.4.24",
|
||||
"core-js": "3.38.1",
|
||||
"core-js": "3.39.0",
|
||||
"css-loader": "6.7.3",
|
||||
"css-modules-typescript-loader": "4.0.1",
|
||||
"eslint": "8.57.1",
|
||||
|
||||
@@ -221,7 +221,7 @@
|
||||
<PropertyGroup Condition="'$(IsOSX)' == 'true' and
|
||||
'$(RuntimeIdentifier)' == ''">
|
||||
<_UsingDefaultRuntimeIdentifier>true</_UsingDefaultRuntimeIdentifier>
|
||||
<RuntimeIdentifier>osx-x64</RuntimeIdentifier>
|
||||
<RuntimeIdentifier>osx-$(Architecture)</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Selenium.Support" Version="4.1.0" />
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="99.0.4844.5100" />
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="134.0.6998.16500" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NzbDrone.Test.Common\Prowlarr.Test.Common.csproj" />
|
||||
|
||||
@@ -21,9 +21,28 @@ namespace NzbDrone.Common.Test.ExtensionTests
|
||||
[TestCase("1.2.3.4")]
|
||||
[TestCase("172.55.0.1")]
|
||||
[TestCase("192.55.0.1")]
|
||||
[TestCase("100.64.0.1")]
|
||||
[TestCase("100.127.255.254")]
|
||||
public void should_return_false_for_public_ip_address(string ipAddress)
|
||||
{
|
||||
IPAddress.Parse(ipAddress).IsLocalAddress().Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestCase("100.64.0.1")]
|
||||
[TestCase("100.127.255.254")]
|
||||
[TestCase("100.100.100.100")]
|
||||
public void should_return_true_for_cgnat_ip_address(string ipAddress)
|
||||
{
|
||||
IPAddress.Parse(ipAddress).IsCgnatIpAddress().Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase("1.2.3.4")]
|
||||
[TestCase("192.168.5.1")]
|
||||
[TestCase("100.63.255.255")]
|
||||
[TestCase("100.128.0.0")]
|
||||
public void should_return_false_for_non_cgnat_ip_address(string ipAddress)
|
||||
{
|
||||
IPAddress.Parse(ipAddress).IsCgnatIpAddress().Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
||||
[TestCase(@"https://anthelion.me/api.php?api_key=2b51db35e1910123321025a12b9933d2&o=json&t=movie&q=&tmdb=&imdb=&cat=&limit=100&offset=0")]
|
||||
[TestCase(@"https://avistaz.to/api/v1/jackett/auth: username=mySecret&password=mySecret&pid=mySecret")]
|
||||
[TestCase(@"https://www.sharewood.tv/api/2b51db35e1910123321025a12b9933d2/last-torrents")]
|
||||
[TestCase(@"https://example.org/rss/torrents?rsskey=2b51db35e1910123321025a12b9933d2&search=")]
|
||||
|
||||
// Indexer and Download Client Responses
|
||||
|
||||
|
||||
@@ -133,11 +133,16 @@ namespace NzbDrone.Common.Test
|
||||
|
||||
[TestCase(@"C:\test\", @"C:\Test\mydir")]
|
||||
[TestCase(@"C:\test", @"C:\Test\mydir\")]
|
||||
public void path_should_be_parent_on_windows_only(string parentPath, string childPath)
|
||||
public void windows_path_should_be_parent(string parentPath, string childPath)
|
||||
{
|
||||
var expectedResult = OsInfo.IsWindows;
|
||||
parentPath.IsParentPath(childPath).Should().Be(true);
|
||||
}
|
||||
|
||||
parentPath.IsParentPath(childPath).Should().Be(expectedResult);
|
||||
[TestCase("/test", "/test/mydir/")]
|
||||
[TestCase("/test/", "/test/mydir")]
|
||||
public void posix_path_should_be_parent(string parentPath, string childPath)
|
||||
{
|
||||
parentPath.IsParentPath(childPath).Should().Be(true);
|
||||
}
|
||||
|
||||
[TestCase(@"C:\Test\mydir", @"C:\Test")]
|
||||
@@ -145,20 +150,57 @@ namespace NzbDrone.Common.Test
|
||||
[TestCase(@"C:\", null)]
|
||||
[TestCase(@"\\server\share", null)]
|
||||
[TestCase(@"\\server\share\test", @"\\server\share")]
|
||||
public void path_should_return_parent_windows(string path, string parentPath)
|
||||
public void windows_path_should_return_parent(string path, string parentPath)
|
||||
{
|
||||
WindowsOnly();
|
||||
path.GetParentPath().Should().Be(parentPath);
|
||||
}
|
||||
|
||||
[TestCase(@"/", null)]
|
||||
[TestCase(@"/test", "/")]
|
||||
public void path_should_return_parent_mono(string path, string parentPath)
|
||||
[TestCase(@"/test/tv", "/test")]
|
||||
public void unix_path_should_return_parent(string path, string parentPath)
|
||||
{
|
||||
PosixOnly();
|
||||
path.GetParentPath().Should().Be(parentPath);
|
||||
}
|
||||
|
||||
[TestCase(@"C:\Test\mydir", "Test")]
|
||||
[TestCase(@"C:\Test\", @"C:\")]
|
||||
[TestCase(@"C:\Test", @"C:\")]
|
||||
[TestCase(@"C:\", null)]
|
||||
[TestCase(@"\\server\share", null)]
|
||||
[TestCase(@"\\server\share\test", @"\\server\share")]
|
||||
public void path_should_return_parent_name_windows(string path, string parentPath)
|
||||
{
|
||||
path.GetParentName().Should().Be(parentPath);
|
||||
}
|
||||
|
||||
[TestCase(@"/", null)]
|
||||
[TestCase(@"/test", "/")]
|
||||
[TestCase(@"/test/tv", "test")]
|
||||
public void path_should_return_parent_name_mono(string path, string parentPath)
|
||||
{
|
||||
path.GetParentName().Should().Be(parentPath);
|
||||
}
|
||||
|
||||
[TestCase(@"C:\Test\mydir", "mydir")]
|
||||
[TestCase(@"C:\Test\", "Test")]
|
||||
[TestCase(@"C:\Test", "Test")]
|
||||
[TestCase(@"C:\", "C:\\")]
|
||||
[TestCase(@"\\server\share", @"\\server\share")]
|
||||
[TestCase(@"\\server\share\test", "test")]
|
||||
public void path_should_return_directory_name_windows(string path, string parentPath)
|
||||
{
|
||||
path.GetDirectoryName().Should().Be(parentPath);
|
||||
}
|
||||
|
||||
[TestCase(@"/", "/")]
|
||||
[TestCase(@"/test", "test")]
|
||||
[TestCase(@"/test/tv", "tv")]
|
||||
public void path_should_return_directory_name_mono(string path, string parentPath)
|
||||
{
|
||||
path.GetDirectoryName().Should().Be(parentPath);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void path_should_return_parent_for_oversized_path()
|
||||
{
|
||||
@@ -166,7 +208,7 @@ namespace NzbDrone.Common.Test
|
||||
|
||||
// This test will fail on Windows if long path support is not enabled: https://www.howtogeek.com/266621/how-to-make-windows-10-accept-file-paths-over-260-characters/
|
||||
// It will also fail if the app isn't configured to use long path (such as resharper): https://blogs.msdn.microsoft.com/jeremykuhne/2016/07/30/net-4-6-2-and-long-paths-on-windows-10/
|
||||
var path = @"C:\media\2e168617-f2ae-43fb-b88c-3663af1c8eea\downloads\sabnzbd\nzbdrone\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories".AsOsAgnostic();
|
||||
var path = @"C:\media\2e168617-f2ae-43fb-b88c-3663af1c8eea\downloads\sabnzbd\nzbdrone\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories".AsOsAgnostic();
|
||||
var parentPath = @"C:\media\2e168617-f2ae-43fb-b88c-3663af1c8eea\downloads\sabnzbd\nzbdrone\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing".AsOsAgnostic();
|
||||
|
||||
path.GetParentPath().Should().Be(parentPath);
|
||||
@@ -350,5 +392,46 @@ namespace NzbDrone.Common.Test
|
||||
PosixOnly();
|
||||
path.AsOsAgnostic().IsPathValid(PathValidationType.CurrentOs).Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestCase(@"C:\", @"C:\")]
|
||||
[TestCase(@"C:\\", @"C:\")]
|
||||
[TestCase(@"C:\Test", @"C:\Test")]
|
||||
[TestCase(@"C:\Test\", @"C:\Test")]
|
||||
[TestCase(@"\\server\share", @"\\server\share")]
|
||||
[TestCase(@"\\server\share\", @"\\server\share")]
|
||||
public void windows_path_should_return_clean_path(string path, string cleanPath)
|
||||
{
|
||||
path.GetCleanPath().Should().Be(cleanPath);
|
||||
}
|
||||
|
||||
[TestCase("/", "/")]
|
||||
[TestCase("//", "/")]
|
||||
[TestCase("/test", "/test")]
|
||||
[TestCase("/test/", "/test")]
|
||||
[TestCase("/test//", "/test")]
|
||||
public void unix_path_should_return_clean_path(string path, string cleanPath)
|
||||
{
|
||||
path.GetCleanPath().Should().Be(cleanPath);
|
||||
}
|
||||
|
||||
[TestCase(@"C:\Test\", @"C:\Test\Series Title", "Series Title")]
|
||||
[TestCase(@"C:\Test\", @"C:\Test\Collection\Series Title", @"Collection\Series Title")]
|
||||
[TestCase(@"C:\Test\mydir\", @"C:\Test\mydir\Collection\Series Title", @"Collection\Series Title")]
|
||||
[TestCase(@"\\server\share", @"\\server\share\Series Title", "Series Title")]
|
||||
[TestCase(@"\\server\share\mydir\", @"\\server\share\mydir\/Collection\Series Title", @"Collection\Series Title")]
|
||||
public void windows_path_should_return_relative_path(string parentPath, string childPath, string relativePath)
|
||||
{
|
||||
parentPath.GetRelativePath(childPath).Should().Be(relativePath);
|
||||
}
|
||||
|
||||
[TestCase(@"/test", "/test/Series Title", "Series Title")]
|
||||
[TestCase(@"/test/", "/test/Collection/Series Title", "Collection/Series Title")]
|
||||
[TestCase(@"/test/mydir", "/test/mydir/Series Title", "Series Title")]
|
||||
[TestCase(@"/test/mydir/", "/test/mydir/Collection/Series Title", "Collection/Series Title")]
|
||||
[TestCase(@"/test/mydir/", @"/test/mydir/\Collection/Series Title", "Collection/Series Title")]
|
||||
public void unix_path_should_return_relative_path(string parentPath, string childPath, string relativePath)
|
||||
{
|
||||
parentPath.GetRelativePath(childPath).Should().Be(relativePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,17 +42,18 @@ namespace NzbDrone.Common
|
||||
|
||||
public void CreateZip(string path, IEnumerable<string> files)
|
||||
{
|
||||
using (var zipFile = ZipFile.Create(path))
|
||||
_logger.Debug("Creating archive {0}", path);
|
||||
|
||||
using var zipFile = ZipFile.Create(path);
|
||||
|
||||
zipFile.BeginUpdate();
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
zipFile.BeginUpdate();
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
zipFile.Add(file, Path.GetFileName(file));
|
||||
}
|
||||
|
||||
zipFile.CommitUpdate();
|
||||
zipFile.Add(file, Path.GetFileName(file));
|
||||
}
|
||||
|
||||
zipFile.CommitUpdate();
|
||||
}
|
||||
|
||||
private void ExtractZip(string compressedFile, string destination)
|
||||
|
||||
@@ -189,6 +189,25 @@ namespace NzbDrone.Common.Disk
|
||||
}
|
||||
|
||||
var fi = new FileInfo(path);
|
||||
|
||||
try
|
||||
{
|
||||
// If the file is a symlink, resolve the target path and get the size of the target file.
|
||||
if (fi.Attributes.HasFlag(FileAttributes.ReparsePoint))
|
||||
{
|
||||
var targetPath = fi.ResolveLinkTarget(true)?.FullName;
|
||||
|
||||
if (targetPath != null)
|
||||
{
|
||||
fi = new FileInfo(targetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Logger.Trace(ex, "Unable to resolve symlink target for {0}", path);
|
||||
}
|
||||
|
||||
return fi.Length;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Common.Disk
|
||||
@@ -9,6 +10,8 @@ namespace NzbDrone.Common.Disk
|
||||
private readonly string _path;
|
||||
private readonly OsPathKind _kind;
|
||||
|
||||
private static readonly Regex UncPathRegex = new Regex(@"(?<unc>^\\\\(?:\?\\UNC\\)?[^\\]+\\[^\\]+)(?:\\|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
public OsPath(string path)
|
||||
{
|
||||
if (path == null)
|
||||
@@ -96,6 +99,29 @@ namespace NzbDrone.Common.Disk
|
||||
return path;
|
||||
}
|
||||
|
||||
private static string TrimTrailingSlash(string path, OsPathKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case OsPathKind.Windows when !path.EndsWith(":\\"):
|
||||
while (!path.EndsWith(":\\") && path.EndsWith('\\'))
|
||||
{
|
||||
path = path[..^1];
|
||||
}
|
||||
|
||||
return path;
|
||||
case OsPathKind.Unix when path != "/":
|
||||
while (path != "/" && path.EndsWith('/'))
|
||||
{
|
||||
path = path[..^1];
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public OsPathKind Kind => _kind;
|
||||
|
||||
public bool IsWindowsPath => _kind == OsPathKind.Windows;
|
||||
@@ -130,7 +156,19 @@ namespace NzbDrone.Common.Disk
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
return new OsPath(null);
|
||||
return Null;
|
||||
}
|
||||
|
||||
var rootLength = GetRootLength();
|
||||
|
||||
if (rootLength == _path.Length)
|
||||
{
|
||||
return Null;
|
||||
}
|
||||
|
||||
if (rootLength > index + 1)
|
||||
{
|
||||
return new OsPath(_path.Substring(0, rootLength));
|
||||
}
|
||||
|
||||
return new OsPath(_path.Substring(0, index), _kind).AsDirectory();
|
||||
@@ -139,6 +177,8 @@ namespace NzbDrone.Common.Disk
|
||||
|
||||
public string FullPath => _path;
|
||||
|
||||
public string PathWithoutTrailingSlash => TrimTrailingSlash(_path, _kind);
|
||||
|
||||
public string FileName
|
||||
{
|
||||
get
|
||||
@@ -161,6 +201,29 @@ namespace NzbDrone.Common.Disk
|
||||
}
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
// Meant to behave similar to DirectoryInfo.Name
|
||||
get
|
||||
{
|
||||
var index = GetFileNameIndex();
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
return PathWithoutTrailingSlash;
|
||||
}
|
||||
|
||||
var rootLength = GetRootLength();
|
||||
|
||||
if (rootLength > index + 1)
|
||||
{
|
||||
return _path.Substring(0, rootLength);
|
||||
}
|
||||
|
||||
return TrimTrailingSlash(_path.Substring(index).TrimStart('/', '\\'), _kind);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsValid => _path.IsPathValid(PathValidationType.CurrentOs);
|
||||
|
||||
private int GetFileNameIndex()
|
||||
@@ -190,11 +253,50 @@ namespace NzbDrone.Common.Disk
|
||||
return index;
|
||||
}
|
||||
|
||||
private int GetRootLength()
|
||||
{
|
||||
if (!IsRooted)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_kind == OsPathKind.Unix)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (_kind == OsPathKind.Windows)
|
||||
{
|
||||
if (HasWindowsDriveLetter(_path))
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
var uncMatch = UncPathRegex.Match(_path);
|
||||
|
||||
// \\?\UNC\server\share\ or \\server\share
|
||||
if (uncMatch.Success)
|
||||
{
|
||||
return uncMatch.Groups["unc"].Length;
|
||||
}
|
||||
|
||||
// \\?\C:\
|
||||
if (_path.StartsWith(@"\\?\"))
|
||||
{
|
||||
return 7;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private string[] GetFragments()
|
||||
{
|
||||
return _path.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
public static OsPath Null => new (null);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _path;
|
||||
@@ -267,6 +369,11 @@ namespace NzbDrone.Common.Disk
|
||||
}
|
||||
|
||||
public bool Equals(OsPath other)
|
||||
{
|
||||
return Equals(other, false);
|
||||
}
|
||||
|
||||
public bool Equals(OsPath other, bool ignoreTrailingSlash)
|
||||
{
|
||||
if (ReferenceEquals(other, null))
|
||||
{
|
||||
@@ -278,8 +385,8 @@ namespace NzbDrone.Common.Disk
|
||||
return true;
|
||||
}
|
||||
|
||||
var left = _path;
|
||||
var right = other._path;
|
||||
var left = ignoreTrailingSlash ? PathWithoutTrailingSlash : _path;
|
||||
var right = ignoreTrailingSlash ? other.PathWithoutTrailingSlash : other._path;
|
||||
|
||||
if (Kind == OsPathKind.Windows || other.Kind == OsPathKind.Windows)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
@@ -25,22 +24,25 @@ namespace NzbDrone.Common.EnvironmentInfo
|
||||
|
||||
static OsInfo()
|
||||
{
|
||||
var platform = Environment.OSVersion.Platform;
|
||||
|
||||
switch (platform)
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
case PlatformID.Win32NT:
|
||||
{
|
||||
Os = Os.Windows;
|
||||
break;
|
||||
}
|
||||
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
Os = GetPosixFlavour();
|
||||
break;
|
||||
}
|
||||
Os = Os.Windows;
|
||||
}
|
||||
else if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
Os = Os.Osx;
|
||||
}
|
||||
else if (OperatingSystem.IsFreeBSD())
|
||||
{
|
||||
Os = Os.Bsd;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if ISMUSL
|
||||
Os = Os.LinuxMusl;
|
||||
#else
|
||||
Os = Os.Linux;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,59 +86,6 @@ namespace NzbDrone.Common.EnvironmentInfo
|
||||
IsDocker = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static Os GetPosixFlavour()
|
||||
{
|
||||
var output = RunAndCapture("uname", "-s");
|
||||
|
||||
if (output.StartsWith("Darwin"))
|
||||
{
|
||||
return Os.Osx;
|
||||
}
|
||||
else if (output.Contains("BSD"))
|
||||
{
|
||||
return Os.Bsd;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if ISMUSL
|
||||
return Os.LinuxMusl;
|
||||
#else
|
||||
return Os.Linux;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private static string RunAndCapture(string filename, string args)
|
||||
{
|
||||
var processStartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = filename,
|
||||
Arguments = args,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardOutput = true
|
||||
};
|
||||
|
||||
var output = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
using (var p = Process.Start(processStartInfo))
|
||||
{
|
||||
// To avoid deadlocks, always read the output stream first and then wait.
|
||||
output = p.StandardOutput.ReadToEnd();
|
||||
|
||||
p.WaitForExit(1000);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
output = string.Empty;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IOsInfo
|
||||
|
||||
@@ -39,18 +39,24 @@ namespace NzbDrone.Common.Extensions
|
||||
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;
|
||||
var 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;
|
||||
var 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;
|
||||
var 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;
|
||||
var isClassC = ipv4Bytes[0] == 192 && ipv4Bytes[1] == 168;
|
||||
|
||||
return IsLinkLocal() || IsClassA() || IsClassC() || IsClassB();
|
||||
return isLinkLocal || isClassA || isClassC || isClassB;
|
||||
}
|
||||
|
||||
public static bool IsCgnatIpAddress(this IPAddress ipAddress)
|
||||
{
|
||||
var bytes = ipAddress.GetAddressBytes();
|
||||
return bytes.Length == 4 && bytes[0] == 100 && bytes[1] >= 64 && bytes[1] <= 127;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,6 @@ namespace NzbDrone.Common.Extensions
|
||||
private static readonly string UPDATE_CLIENT_FOLDER_NAME = "Prowlarr.Update" + Path.DirectorySeparatorChar;
|
||||
private static readonly string UPDATE_LOG_FOLDER_NAME = "UpdateLogs" + Path.DirectorySeparatorChar;
|
||||
|
||||
private static readonly Regex PARENT_PATH_END_SLASH_REGEX = new Regex(@"(?<!:)\\$", RegexOptions.Compiled);
|
||||
|
||||
public static string CleanFilePath(this string path)
|
||||
{
|
||||
if (path.IsNotNullOrWhiteSpace())
|
||||
@@ -87,55 +85,50 @@ namespace NzbDrone.Common.Extensions
|
||||
throw new NotParentException("{0} is not a child of {1}", childPath, parentPath);
|
||||
}
|
||||
|
||||
return childPath.Substring(parentPath.Length).Trim(Path.DirectorySeparatorChar);
|
||||
return childPath.Substring(parentPath.Length).Trim('\\', '/');
|
||||
}
|
||||
|
||||
public static string GetParentPath(this string childPath)
|
||||
{
|
||||
var cleanPath = OsInfo.IsWindows
|
||||
? PARENT_PATH_END_SLASH_REGEX.Replace(childPath, "")
|
||||
: childPath.TrimEnd(Path.DirectorySeparatorChar);
|
||||
var path = new OsPath(childPath).Directory;
|
||||
|
||||
if (cleanPath.IsNullOrWhiteSpace())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return path == OsPath.Null ? null : path.PathWithoutTrailingSlash;
|
||||
}
|
||||
|
||||
return Directory.GetParent(cleanPath)?.FullName;
|
||||
public static string GetParentName(this string childPath)
|
||||
{
|
||||
var path = new OsPath(childPath).Directory;
|
||||
|
||||
return path == OsPath.Null ? null : path.Name;
|
||||
}
|
||||
|
||||
public static string GetDirectoryName(this string childPath)
|
||||
{
|
||||
var path = new OsPath(childPath);
|
||||
|
||||
return path == OsPath.Null ? null : path.Name;
|
||||
}
|
||||
|
||||
public static string GetCleanPath(this string path)
|
||||
{
|
||||
var cleanPath = OsInfo.IsWindows
|
||||
? PARENT_PATH_END_SLASH_REGEX.Replace(path, "")
|
||||
: path.TrimEnd(Path.DirectorySeparatorChar);
|
||||
var osPath = new OsPath(path);
|
||||
|
||||
return cleanPath;
|
||||
return osPath == OsPath.Null ? null : osPath.PathWithoutTrailingSlash;
|
||||
}
|
||||
|
||||
public static bool IsParentPath(this string parentPath, string childPath)
|
||||
{
|
||||
if (parentPath != "/" && !parentPath.EndsWith(":\\"))
|
||||
{
|
||||
parentPath = parentPath.TrimEnd(Path.DirectorySeparatorChar);
|
||||
}
|
||||
var parent = new OsPath(parentPath);
|
||||
var child = new OsPath(childPath);
|
||||
|
||||
if (childPath != "/" && !parentPath.EndsWith(":\\"))
|
||||
while (child.Directory != OsPath.Null)
|
||||
{
|
||||
childPath = childPath.TrimEnd(Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
var parent = new DirectoryInfo(parentPath);
|
||||
var child = new DirectoryInfo(childPath);
|
||||
|
||||
while (child.Parent != null)
|
||||
{
|
||||
if (child.Parent.FullName.Equals(parent.FullName, DiskProviderBase.PathStringComparison))
|
||||
if (child.Directory.Equals(parent, true))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
child = child.Parent;
|
||||
child = child.Directory;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -150,14 +143,14 @@ namespace NzbDrone.Common.Extensions
|
||||
return false;
|
||||
}
|
||||
|
||||
if (path.Trim() != path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only check for leading or trailing spaces for path when running on Windows.
|
||||
if (OsInfo.IsWindows)
|
||||
{
|
||||
if (path.Trim() != path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var directoryInfo = new DirectoryInfo(path);
|
||||
|
||||
while (directoryInfo != null)
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
private static readonly Regex[] CleansingRules =
|
||||
{
|
||||
// Url
|
||||
new (@"(?<=[?&: ;])(apikey|api_key|(?:(?:access|api)[-_]?)?token|pass(?:key|wd)?|auth|authkey|user|u?id|api|[a-z_]*apikey|account|pid|pwd)=(?<secret>[^&=""]+?)(?=[ ""&=]|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"(?<=[?&: ;])(apikey|api_key|(?:(?:access|api)[-_]?)?token|pass(?:key|wd)?|auth|authkey|rsskey|user|u?id|api|[a-z_]*apikey|account|pid|pwd)=(?<secret>[^&=""]+?)(?=[ ""&=]|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"(?<=[?& ;])[^=]*?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"rss(24h)?\.torrentleech\.org/(?!rss)(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new (@"torrentleech\.org/rss/download/[0-9]+/(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NLog.Layouts.ClefJsonLayout;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
|
||||
namespace NzbDrone.Common.Instrumentation;
|
||||
|
||||
public class CleansingClefLogLayout : CompactJsonLayout
|
||||
{
|
||||
protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target)
|
||||
{
|
||||
base.RenderFormattedMessage(logEvent, target);
|
||||
|
||||
if (RuntimeInfo.IsProduction)
|
||||
{
|
||||
var result = CleanseLogMessage.Cleanse(target.ToString());
|
||||
target.Clear();
|
||||
target.Append(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NLog.Layouts;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
|
||||
namespace NzbDrone.Common.Instrumentation;
|
||||
|
||||
public class CleansingConsoleLogLayout : SimpleLayout
|
||||
{
|
||||
public CleansingConsoleLogLayout(string format)
|
||||
: base(format)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target)
|
||||
{
|
||||
base.RenderFormattedMessage(logEvent, target);
|
||||
|
||||
if (RuntimeInfo.IsProduction)
|
||||
{
|
||||
var result = CleanseLogMessage.Cleanse(target.ToString());
|
||||
target.Clear();
|
||||
target.Append(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using NLog.Targets;
|
||||
|
||||
namespace NzbDrone.Common.Instrumentation
|
||||
{
|
||||
public class NzbDroneFileTarget : FileTarget
|
||||
public class CleansingFileTarget : FileTarget
|
||||
{
|
||||
protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target)
|
||||
{
|
||||
@@ -3,7 +3,6 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using NLog;
|
||||
using NLog.Config;
|
||||
using NLog.Layouts.ClefJsonLayout;
|
||||
using NLog.Targets;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -13,9 +12,11 @@ namespace NzbDrone.Common.Instrumentation
|
||||
{
|
||||
public static class NzbDroneLogger
|
||||
{
|
||||
private const string FILE_LOG_LAYOUT = @"${date:format=yyyy-MM-dd HH\:mm\:ss.f}|${level}|${logger}|${message}${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}";
|
||||
public const string ConsoleLogLayout = "[${level}] ${logger}: ${message} ${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}";
|
||||
public static CompactJsonLayout ClefLogLayout = new CompactJsonLayout();
|
||||
private const string FileLogLayout = @"${date:format=yyyy-MM-dd HH\:mm\:ss.f}|${level}|${logger}|${message}${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}";
|
||||
private const string ConsoleFormat = "[${level}] ${logger}: ${message} ${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}";
|
||||
|
||||
private static readonly CleansingConsoleLogLayout CleansingConsoleLayout = new (ConsoleFormat);
|
||||
private static readonly CleansingClefLogLayout ClefLogLayout = new ();
|
||||
|
||||
private static bool _isConfigured;
|
||||
|
||||
@@ -119,11 +120,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
? formatEnumValue
|
||||
: ConsoleLogFormat.Standard;
|
||||
|
||||
coloredConsoleTarget.Layout = logFormat switch
|
||||
{
|
||||
ConsoleLogFormat.Clef => ClefLogLayout,
|
||||
_ => ConsoleLogLayout
|
||||
};
|
||||
ConfigureConsoleLayout(coloredConsoleTarget, logFormat);
|
||||
|
||||
var loggingRule = new LoggingRule("*", level, coloredConsoleTarget);
|
||||
|
||||
@@ -140,7 +137,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
|
||||
private static void RegisterAppFile(IAppFolderInfo appFolderInfo, string name, string fileName, int maxArchiveFiles, LogLevel minLogLevel)
|
||||
{
|
||||
var fileTarget = new NzbDroneFileTarget();
|
||||
var fileTarget = new CleansingFileTarget();
|
||||
|
||||
fileTarget.Name = name;
|
||||
fileTarget.FileName = Path.Combine(appFolderInfo.GetLogFolder(), fileName);
|
||||
@@ -153,7 +150,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
fileTarget.MaxArchiveFiles = maxArchiveFiles;
|
||||
fileTarget.EnableFileDelete = true;
|
||||
fileTarget.ArchiveNumbering = ArchiveNumberingMode.Rolling;
|
||||
fileTarget.Layout = FILE_LOG_LAYOUT;
|
||||
fileTarget.Layout = FileLogLayout;
|
||||
|
||||
var loggingRule = new LoggingRule("*", minLogLevel, fileTarget);
|
||||
|
||||
@@ -172,7 +169,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
fileTarget.ConcurrentWrites = false;
|
||||
fileTarget.ConcurrentWriteAttemptDelay = 50;
|
||||
fileTarget.ConcurrentWriteAttempts = 100;
|
||||
fileTarget.Layout = FILE_LOG_LAYOUT;
|
||||
fileTarget.Layout = FileLogLayout;
|
||||
|
||||
var loggingRule = new LoggingRule("*", LogLevel.Trace, fileTarget);
|
||||
|
||||
@@ -217,6 +214,15 @@ namespace NzbDrone.Common.Instrumentation
|
||||
{
|
||||
return GetLogger(obj.GetType());
|
||||
}
|
||||
|
||||
public static void ConfigureConsoleLayout(ColoredConsoleTarget target, ConsoleLogFormat format)
|
||||
{
|
||||
target.Layout = format switch
|
||||
{
|
||||
ConsoleLogFormat.Clef => NzbDroneLogger.ClefLogLayout,
|
||||
_ => NzbDroneLogger.CleansingConsoleLayout
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public enum ConsoleLogFormat
|
||||
|
||||
@@ -119,7 +119,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||
o.Environment = BuildInfo.Branch;
|
||||
|
||||
// Crash free run statistics (sends a ping for healthy and for crashes sessions)
|
||||
o.AutoSessionTracking = true;
|
||||
o.AutoSessionTracking = false;
|
||||
|
||||
// Caches files in the event device is offline
|
||||
// Sentry creates a 'sentry' sub directory, no need to concat here
|
||||
@@ -148,7 +148,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||
_debounce = new SentryDebounce();
|
||||
|
||||
// initialize to true and reconfigure later
|
||||
// Otherwise it will default to false and any errors occuring
|
||||
// Otherwise it will default to false and any errors occurring
|
||||
// before config file gets read will not be filtered
|
||||
FilterEvents = true;
|
||||
SentryEnabled = true;
|
||||
@@ -207,9 +207,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||
|
||||
private void OnError(Exception ex)
|
||||
{
|
||||
var webException = ex as WebException;
|
||||
|
||||
if (webException != null)
|
||||
if (ex is WebException webException)
|
||||
{
|
||||
var response = webException.Response as HttpWebResponse;
|
||||
var statusCode = response?.StatusCode;
|
||||
|
||||
@@ -6,4 +6,5 @@ public class AuthOptions
|
||||
public bool? Enabled { get; set; }
|
||||
public string Method { get; set; }
|
||||
public string Required { get; set; }
|
||||
public bool? TrustCgnatIpAddresses { get; set; }
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Model;
|
||||
@@ -117,7 +118,9 @@ namespace NzbDrone.Common.Processes
|
||||
UseShellExecute = false,
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardInput = true
|
||||
RedirectStandardInput = true,
|
||||
StandardOutputEncoding = Encoding.UTF8,
|
||||
StandardErrorEncoding = Encoding.UTF8
|
||||
};
|
||||
|
||||
if (environmentVariables != null)
|
||||
@@ -313,7 +316,7 @@ namespace NzbDrone.Common.Processes
|
||||
processInfo = new ProcessInfo();
|
||||
processInfo.Id = process.Id;
|
||||
processInfo.Name = process.ProcessName;
|
||||
processInfo.StartPath = process.MainModule.FileName;
|
||||
processInfo.StartPath = process.MainModule?.FileName;
|
||||
|
||||
if (process.Id != GetCurrentProcessId() && process.HasExited)
|
||||
{
|
||||
|
||||
@@ -5,20 +5,21 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DryIoc.dll" Version="5.4.3" />
|
||||
<PackageReference Include="IPAddressRange" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.2" />
|
||||
<PackageReference Include="IPAddressRange" Version="6.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="NLog" Version="5.3.3" />
|
||||
<PackageReference Include="NLog.Layouts.ClefJsonLayout" Version="1.0.0" />
|
||||
<PackageReference Include="NLog.Extensions.Logging" Version="5.3.12" />
|
||||
<PackageReference Include="Npgsql" Version="7.0.8" />
|
||||
<PackageReference Include="NLog" Version="5.4.0" />
|
||||
<PackageReference Include="NLog.Layouts.ClefJsonLayout" Version="1.0.3" />
|
||||
<PackageReference Include="NLog.Extensions.Logging" Version="5.4.0" />
|
||||
<PackageReference Include="Npgsql" Version="7.0.10" />
|
||||
<PackageReference Include="Sentry" Version="4.0.2" />
|
||||
<PackageReference Include="NLog.Targets.Syslog" Version="7.0.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
<PackageReference Include="System.Text.Json" Version="6.0.11" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.6.1" />
|
||||
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.1" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.2" />
|
||||
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
|
||||
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
|
||||
<PackageReference Include="System.ServiceProcess.ServiceController" Version="6.0.1" />
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Data.SQLite;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore.Converters;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Converters;
|
||||
|
||||
[TestFixture]
|
||||
public class TimeSpanConverterFixture : CoreTest<TimeSpanConverter>
|
||||
{
|
||||
private SQLiteParameter _param;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_param = new SQLiteParameter();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_string_when_saving_timespan_to_db()
|
||||
{
|
||||
var span = TimeSpan.FromMilliseconds(10);
|
||||
|
||||
Subject.SetValue(_param, span);
|
||||
_param.Value.Should().Be(span.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_timespan_when_getting_string_from_db()
|
||||
{
|
||||
var span = TimeSpan.FromMilliseconds(10);
|
||||
|
||||
Subject.Parse(span.ToString()).Should().Be(span);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_zero_timespan_for_db_null_value_when_getting_from_db()
|
||||
{
|
||||
Subject.Parse(null).Should().Be(TimeSpan.Zero);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore;
|
||||
|
||||
[TestFixture]
|
||||
public class DatabaseVersionParserFixture
|
||||
{
|
||||
[TestCase("3.44.2", 3, 44, 2)]
|
||||
public void should_parse_sqlite_database_version(string serverVersion, int majorVersion, int minorVersion, int buildVersion)
|
||||
{
|
||||
var version = DatabaseVersionParser.ParseServerVersion(serverVersion);
|
||||
|
||||
version.Should().NotBeNull();
|
||||
version.Major.Should().Be(majorVersion);
|
||||
version.Minor.Should().Be(minorVersion);
|
||||
version.Build.Should().Be(buildVersion);
|
||||
}
|
||||
|
||||
[TestCase("14.8 (Debian 14.8-1.pgdg110+1)", 14, 8, null)]
|
||||
[TestCase("16.3 (Debian 16.3-1.pgdg110+1)", 16, 3, null)]
|
||||
[TestCase("16.3 - Percona Distribution", 16, 3, null)]
|
||||
[TestCase("17.0 - Percona Server", 17, 0, null)]
|
||||
public void should_parse_postgres_database_version(string serverVersion, int majorVersion, int minorVersion, int? buildVersion)
|
||||
{
|
||||
var version = DatabaseVersionParser.ParseServerVersion(serverVersion);
|
||||
|
||||
version.Should().NotBeNull();
|
||||
version.Major.Should().Be(majorVersion);
|
||||
version.Minor.Should().Be(minorVersion);
|
||||
|
||||
if (buildVersion.HasValue)
|
||||
{
|
||||
version.Build.Should().Be(buildVersion.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AnimeBytesTests
|
||||
|
||||
var fifthTorrentInfo = releases.ElementAt(28) as TorrentInfo;
|
||||
|
||||
fifthTorrentInfo.Title.Should().Be("[-ZR-] Dr. STONE: STONE WARS S02 [Web][MKV][h264][1080p][AAC 2.0][Dual Audio][Softsubs (-ZR-)]");
|
||||
fifthTorrentInfo.Title.Should().Be("[-ZR-] Dr. STONE: STONE WARS 2021 S02 [Web][MKV][h264][1080p][AAC 2.0][Dual Audio][Softsubs (-ZR-)]");
|
||||
fifthTorrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
fifthTorrentInfo.DownloadUrl.Should().Be("https://animebytes.tv/torrent/944509/download/somepass");
|
||||
fifthTorrentInfo.InfoUrl.Should().Be("https://animebytes.tv/torrent/944509/group");
|
||||
|
||||
@@ -26,15 +26,15 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Subject.Definition = new IndexerDefinition()
|
||||
Subject.Definition = new IndexerDefinition
|
||||
{
|
||||
Name = "HdBits",
|
||||
Settings = new HDBitsSettings() { ApiKey = "fakekey" }
|
||||
Settings = new HDBitsSettings { ApiKey = "fakekey" }
|
||||
};
|
||||
|
||||
_movieSearchCriteria = new MovieSearchCriteria
|
||||
{
|
||||
Categories = new int[] { 2000, 2010 },
|
||||
Categories = new[] { 2000, 2010 },
|
||||
ImdbId = "0076759"
|
||||
};
|
||||
}
|
||||
@@ -52,7 +52,7 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests
|
||||
var torrents = (await Subject.Fetch(_movieSearchCriteria)).Releases;
|
||||
|
||||
torrents.Should().HaveCount(2);
|
||||
torrents.First().Should().BeOfType<HDBitsInfo>();
|
||||
torrents.First().Should().BeOfType<TorrentInfo>();
|
||||
|
||||
var first = torrents.First() as TorrentInfo;
|
||||
|
||||
|
||||
@@ -46,8 +46,8 @@ namespace NzbDrone.Core.Test.IndexerTests.RedactedTests
|
||||
|
||||
torrentInfo.Title.Should().Be("Red Hot Chili Peppers - Californication (1999) [Album] [US / Reissue 2020] [FLAC 24bit Lossless / Vinyl]");
|
||||
torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
torrentInfo.DownloadUrl.Should().Be("https://redacted.ch/ajax.php?action=download&id=3892313");
|
||||
torrentInfo.InfoUrl.Should().Be("https://redacted.ch/torrents.php?id=16720&torrentid=3892313");
|
||||
torrentInfo.DownloadUrl.Should().Be("https://redacted.sh/ajax.php?action=download&id=3892313");
|
||||
torrentInfo.InfoUrl.Should().Be("https://redacted.sh/torrents.php?id=16720&torrentid=3892313");
|
||||
torrentInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2022-12-17 08:02:35"));
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace NzbDrone.Core.Applications
|
||||
|
||||
foreach (var application in applications)
|
||||
{
|
||||
if (blockedApplications.TryGetValue(application.Definition.Id, out var blockedApplicationStatus))
|
||||
if (blockedApplications.TryGetValue(application.Definition.Id, out var blockedApplicationStatus) && blockedApplicationStatus.DisabledTill.HasValue)
|
||||
{
|
||||
_logger.Debug("Temporarily ignoring application {0} till {1} due to recent failures.", application.Definition.Name, blockedApplicationStatus.DisabledTill.Value.ToLocalTime());
|
||||
continue;
|
||||
|
||||
@@ -66,12 +66,19 @@ namespace NzbDrone.Core.Backup
|
||||
{
|
||||
_logger.ProgressInfo("Starting Backup");
|
||||
|
||||
var backupFolder = GetBackupFolder(backupType);
|
||||
|
||||
_diskProvider.EnsureFolder(_backupTempFolder);
|
||||
_diskProvider.EnsureFolder(GetBackupFolder(backupType));
|
||||
_diskProvider.EnsureFolder(backupFolder);
|
||||
|
||||
if (!_diskProvider.FolderWritable(backupFolder))
|
||||
{
|
||||
throw new UnauthorizedAccessException($"Backup folder {backupFolder} is not writable");
|
||||
}
|
||||
|
||||
var dateNow = DateTime.Now;
|
||||
var backupFilename = $"prowlarr_backup_v{BuildInfo.Version}_{dateNow:yyyy.MM.dd_HH.mm.ss}.zip";
|
||||
var backupPath = Path.Combine(GetBackupFolder(backupType), backupFilename);
|
||||
var backupPath = Path.Combine(backupFolder, backupFilename);
|
||||
|
||||
Cleanup();
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ namespace NzbDrone.Core.Configuration
|
||||
string PostgresPassword { get; }
|
||||
string PostgresMainDb { get; }
|
||||
string PostgresLogDb { get; }
|
||||
bool TrustCgnatIpAddresses { get; }
|
||||
}
|
||||
|
||||
public class ConfigFileProvider : IConfigFileProvider
|
||||
@@ -273,7 +274,7 @@ namespace NzbDrone.Core.Configuration
|
||||
{
|
||||
var instanceName = _appOptions.InstanceName ?? GetValue("InstanceName", BuildInfo.AppName);
|
||||
|
||||
if (instanceName.ContainsIgnoreCase(BuildInfo.AppName))
|
||||
if (instanceName.Contains(BuildInfo.AppName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return instanceName;
|
||||
}
|
||||
@@ -479,5 +480,7 @@ namespace NzbDrone.Core.Configuration
|
||||
SetValue("ApiKey", GenerateApiKey());
|
||||
_eventAggregator.PublishEvent(new ApiKeyChangedEvent());
|
||||
}
|
||||
|
||||
public bool TrustCgnatIpAddresses => _authOptions.TrustCgnatIpAddresses ?? GetValueBoolean("TrustCgnatIpAddresses", false, persist: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,6 +183,12 @@ namespace NzbDrone.Core.Configuration
|
||||
|
||||
public string ApplicationUrl => GetValue("ApplicationUrl", string.Empty);
|
||||
|
||||
public bool TrustCgnatIpAddresses
|
||||
{
|
||||
get { return GetValueBoolean("TrustCgnatIpAddresses", false); }
|
||||
set { SetValue("TrustCgnatIpAddresses", value); }
|
||||
}
|
||||
|
||||
private string GetValue(string key)
|
||||
{
|
||||
return GetValue(key, string.Empty);
|
||||
|
||||
@@ -254,7 +254,7 @@ namespace NzbDrone.Core.Datastore
|
||||
|
||||
protected void Delete(SqlBuilder builder)
|
||||
{
|
||||
var sql = builder.AddDeleteTemplate(typeof(TModel)).LogQuery();
|
||||
var sql = builder.AddDeleteTemplate(typeof(TModel));
|
||||
|
||||
using (var conn = _database.OpenConnection())
|
||||
{
|
||||
|
||||
@@ -2,18 +2,17 @@ using System;
|
||||
using System.Data;
|
||||
using Dapper;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Converters
|
||||
{
|
||||
public class DapperTimeSpanConverter : SqlMapper.TypeHandler<TimeSpan>
|
||||
{
|
||||
public override void SetValue(IDbDataParameter parameter, TimeSpan value)
|
||||
{
|
||||
parameter.Value = value.ToString();
|
||||
}
|
||||
namespace NzbDrone.Core.Datastore.Converters;
|
||||
|
||||
public override TimeSpan Parse(object value)
|
||||
{
|
||||
return TimeSpan.Parse((string)value);
|
||||
}
|
||||
public class TimeSpanConverter : SqlMapper.TypeHandler<TimeSpan>
|
||||
{
|
||||
public override void SetValue(IDbDataParameter parameter, TimeSpan value)
|
||||
{
|
||||
parameter.Value = value.ToString();
|
||||
}
|
||||
|
||||
public override TimeSpan Parse(object value)
|
||||
{
|
||||
return value is string str ? TimeSpan.Parse(str) : TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.SQLite;
|
||||
using System.Text.RegularExpressions;
|
||||
using Dapper;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Instrumentation;
|
||||
@@ -52,9 +51,8 @@ namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
using var db = _datamapperFactory();
|
||||
var dbConnection = db as DbConnection;
|
||||
var version = Regex.Replace(dbConnection.ServerVersion, @"\(.*?\)", "");
|
||||
|
||||
return new Version(version);
|
||||
return DatabaseVersionParser.ParseServerVersion(dbConnection.ServerVersion);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
16
src/NzbDrone.Core/Datastore/DatabaseVersionParser.cs
Normal file
16
src/NzbDrone.Core/Datastore/DatabaseVersionParser.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace NzbDrone.Core.Datastore;
|
||||
|
||||
public static class DatabaseVersionParser
|
||||
{
|
||||
private static readonly Regex VersionRegex = new (@"^[^ ]+", RegexOptions.Compiled);
|
||||
|
||||
public static Version ParseServerVersion(string serverVersion)
|
||||
{
|
||||
var match = VersionRegex.Match(serverVersion);
|
||||
|
||||
return match.Success ? new Version(match.Value) : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using Dapper;
|
||||
using FluentMigrator;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(042)]
|
||||
public class myanonamouse_freeleech_wedge_options : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Execute.WithConnection(MigrateIndexersToWedgeOptions);
|
||||
}
|
||||
|
||||
private void MigrateIndexersToWedgeOptions(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
var updated = new List<object>();
|
||||
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.Transaction = tran;
|
||||
cmd.CommandText = "SELECT \"Id\", \"Settings\" FROM \"Indexers\" WHERE \"Implementation\" = 'MyAnonamouse'";
|
||||
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
var id = reader.GetInt32(0);
|
||||
var settings = Json.Deserialize<JObject>(reader.GetString(1));
|
||||
|
||||
if (settings.ContainsKey("freeleech") && settings.Value<JToken>("freeleech").Type == JTokenType.Boolean)
|
||||
{
|
||||
var optionValue = settings.Value<bool>("freeleech") switch
|
||||
{
|
||||
true => 2, // Required
|
||||
_ => 0 // Never
|
||||
};
|
||||
|
||||
settings.Remove("freeleech");
|
||||
settings.Add("useFreeleechWedge", optionValue);
|
||||
}
|
||||
|
||||
updated.Add(new
|
||||
{
|
||||
Id = id,
|
||||
Settings = settings.ToJson()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var updateSql = "UPDATE \"Indexers\" SET \"Settings\" = @Settings WHERE \"Id\" = @Id";
|
||||
conn.Execute(updateSql, updated, transaction: tran);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,7 +109,6 @@ namespace NzbDrone.Core.Datastore
|
||||
|
||||
SqlMapper.RemoveTypeMap(typeof(DateTime));
|
||||
SqlMapper.AddTypeHandler(new DapperUtcConverter());
|
||||
SqlMapper.AddTypeHandler(new DapperTimeSpanConverter());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<Dictionary<string, string>>());
|
||||
SqlMapper.AddTypeHandler(new CookieConverter());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<int>>());
|
||||
@@ -123,6 +122,9 @@ namespace NzbDrone.Core.Datastore
|
||||
SqlMapper.RemoveTypeMap(typeof(Guid));
|
||||
SqlMapper.RemoveTypeMap(typeof(Guid?));
|
||||
SqlMapper.AddTypeHandler(new GuidConverter());
|
||||
SqlMapper.RemoveTypeMap(typeof(TimeSpan));
|
||||
SqlMapper.RemoveTypeMap(typeof(TimeSpan?));
|
||||
SqlMapper.AddTypeHandler(new TimeSpanConverter());
|
||||
SqlMapper.AddTypeHandler(new CommandConverter());
|
||||
SqlMapper.AddTypeHandler(new SystemVersionConverter());
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
@@ -25,8 +26,9 @@ namespace NzbDrone.Core.Download.Clients.Aria2
|
||||
ISeedConfigProvider seedConfigProvider,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
_proxy = proxy;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||
@@ -20,8 +21,9 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||
ISeedConfigProvider seedConfigProvider,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||
@@ -16,8 +17,9 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||
public UsenetBlackhole(IHttpClient httpClient,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(httpClient, configService, diskProvider, logger)
|
||||
: base(httpClient, configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
@@ -23,8 +24,9 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
ISeedConfigProvider seedConfigProvider,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
_proxy = proxy;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Download.Clients.DownloadStation.Proxies;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
@@ -33,8 +34,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||
ISeedConfigProvider seedConfigProvider,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
_dsInfoProxy = dsInfoProxy;
|
||||
_dsTaskProxySelector = dsTaskProxySelector;
|
||||
|
||||
@@ -9,6 +9,7 @@ using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Download.Clients.DownloadStation.Proxies;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
@@ -31,8 +32,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||
IHttpClient httpClient,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(httpClient, configService, diskProvider, logger)
|
||||
: base(httpClient, configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
_dsInfoProxy = dsInfoProxy;
|
||||
_dsTaskProxySelector = dsTaskProxySelector;
|
||||
|
||||
@@ -8,6 +8,7 @@ using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Download.Clients.Flood.Models;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
@@ -22,8 +23,9 @@ namespace NzbDrone.Core.Download.Clients.Flood
|
||||
ISeedConfigProvider seedConfigProvider,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
_proxy = proxy;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Download.Clients.FreeboxDownload
|
||||
@@ -19,8 +20,9 @@ namespace NzbDrone.Core.Download.Clients.FreeboxDownload
|
||||
ISeedConfigProvider seedConfigProvider,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
_proxy = proxy;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
@@ -20,8 +21,9 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||
ISeedConfigProvider seedConfigProvider,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
_proxy = proxy;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
@@ -20,8 +21,9 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
|
||||
IHttpClient httpClient,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(httpClient, configService, diskProvider, logger)
|
||||
: base(httpClient, configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
_proxy = proxy;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
@@ -18,15 +19,14 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||
public class Nzbget : UsenetClientBase<NzbgetSettings>
|
||||
{
|
||||
private readonly INzbgetProxy _proxy;
|
||||
private readonly string[] _successStatus = { "SUCCESS", "NONE" };
|
||||
private readonly string[] _deleteFailedStatus = { "HEALTH", "DUPE", "SCAN", "COPY", "BAD" };
|
||||
|
||||
public Nzbget(INzbgetProxy proxy,
|
||||
IHttpClient httpClient,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(httpClient, configService, diskProvider, logger)
|
||||
: base(httpClient, configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
_proxy = proxy;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
@@ -17,8 +18,9 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
|
||||
{
|
||||
public Pneumatic(IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(configService, diskProvider, logger)
|
||||
: base(configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
@@ -30,8 +31,9 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ICacheManager cacheManager,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
_proxySelector = proxySelector;
|
||||
|
||||
|
||||
@@ -25,8 +25,6 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
Dictionary<string, QBittorrentLabel> GetLabels(QBittorrentSettings settings);
|
||||
void SetTorrentSeedingConfiguration(string hash, TorrentSeedConfiguration seedConfiguration, QBittorrentSettings settings);
|
||||
void MoveTorrentToTopInQueue(string hash, QBittorrentSettings settings);
|
||||
void PauseTorrent(string hash, QBittorrentSettings settings);
|
||||
void ResumeTorrent(string hash, QBittorrentSettings settings);
|
||||
void SetForceStart(string hash, bool enabled, QBittorrentSettings settings);
|
||||
}
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
{
|
||||
request.AddFormParameter("paused", false);
|
||||
}
|
||||
else if ((QBittorrentState)settings.InitialState == QBittorrentState.Pause)
|
||||
else if ((QBittorrentState)settings.InitialState == QBittorrentState.Stop)
|
||||
{
|
||||
request.AddFormParameter("paused", true);
|
||||
}
|
||||
@@ -176,7 +176,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
{
|
||||
request.AddFormParameter("paused", false);
|
||||
}
|
||||
else if ((QBittorrentState)settings.InitialState == QBittorrentState.Pause)
|
||||
else if ((QBittorrentState)settings.InitialState == QBittorrentState.Stop)
|
||||
{
|
||||
request.AddFormParameter("paused", true);
|
||||
}
|
||||
@@ -212,7 +212,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
catch (DownloadClientException ex)
|
||||
{
|
||||
// if setCategory fails due to method not being found, then try older setLabel command for qBittorrent < v.3.3.5
|
||||
if (ex.InnerException is HttpException && (ex.InnerException as HttpException).Response.StatusCode == HttpStatusCode.NotFound)
|
||||
if (ex.InnerException is HttpException httpException && httpException.Response.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
var setLabelRequest = BuildRequest(settings).Resource("/command/setLabel")
|
||||
.Post()
|
||||
@@ -254,7 +254,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
catch (DownloadClientException ex)
|
||||
{
|
||||
// qBittorrent rejects all Prio commands with 403: Forbidden if Options -> BitTorrent -> Torrent Queueing is not enabled
|
||||
if (ex.InnerException is HttpException && (ex.InnerException as HttpException).Response.StatusCode == HttpStatusCode.Forbidden)
|
||||
if (ex.InnerException is HttpException httpException && httpException.Response.StatusCode == HttpStatusCode.Forbidden)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -263,22 +263,6 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
}
|
||||
}
|
||||
|
||||
public void PauseTorrent(string hash, QBittorrentSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings).Resource("/command/pause")
|
||||
.Post()
|
||||
.AddFormParameter("hash", hash);
|
||||
ProcessRequest(request, settings);
|
||||
}
|
||||
|
||||
public void ResumeTorrent(string hash, QBittorrentSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings).Resource("/command/resume")
|
||||
.Post()
|
||||
.AddFormParameter("hash", hash);
|
||||
ProcessRequest(request, settings);
|
||||
}
|
||||
|
||||
public void SetForceStart(string hash, bool enabled, QBittorrentSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings).Resource("/command/setForceStart")
|
||||
|
||||
@@ -246,14 +246,20 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
request.AddFormParameter("category", category);
|
||||
}
|
||||
|
||||
// Note: ForceStart is handled by separate api call
|
||||
if ((QBittorrentState)settings.InitialState == QBittorrentState.Start)
|
||||
// Avoid extraneous API version check if initial state is ForceStart
|
||||
if ((QBittorrentState)settings.InitialState is QBittorrentState.Start or QBittorrentState.Stop)
|
||||
{
|
||||
request.AddFormParameter("paused", false);
|
||||
}
|
||||
else if ((QBittorrentState)settings.InitialState == QBittorrentState.Pause)
|
||||
{
|
||||
request.AddFormParameter("paused", true);
|
||||
var stoppedParameterName = GetApiVersion(settings) >= new Version(2, 11, 0) ? "stopped" : "paused";
|
||||
|
||||
// Note: ForceStart is handled by separate api call
|
||||
if ((QBittorrentState)settings.InitialState == QBittorrentState.Start)
|
||||
{
|
||||
request.AddFormParameter(stoppedParameterName, false);
|
||||
}
|
||||
else if ((QBittorrentState)settings.InitialState == QBittorrentState.Stop)
|
||||
{
|
||||
request.AddFormParameter(stoppedParameterName, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.SequentialOrder)
|
||||
@@ -291,7 +297,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
catch (DownloadClientException ex)
|
||||
{
|
||||
// setShareLimits was added in api v2.0.1 so catch it case of the unlikely event that someone has api v2.0
|
||||
if (ex.InnerException is HttpException && (ex.InnerException as HttpException).Response.StatusCode == HttpStatusCode.NotFound)
|
||||
if (ex.InnerException is HttpException httpException && httpException.Response.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -313,7 +319,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
catch (DownloadClientException ex)
|
||||
{
|
||||
// qBittorrent rejects all Prio commands with 409: Conflict if Options -> BitTorrent -> Torrent Queueing is not enabled
|
||||
if (ex.InnerException is HttpException && (ex.InnerException as HttpException).Response.StatusCode == HttpStatusCode.Conflict)
|
||||
if (ex.InnerException is HttpException httpException && httpException.Response.StatusCode == HttpStatusCode.Conflict)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -322,22 +328,6 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
}
|
||||
}
|
||||
|
||||
public void PauseTorrent(string hash, QBittorrentSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings).Resource("/api/v2/torrents/pause")
|
||||
.Post()
|
||||
.AddFormParameter("hashes", hash);
|
||||
ProcessRequest(request, settings);
|
||||
}
|
||||
|
||||
public void ResumeTorrent(string hash, QBittorrentSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings).Resource("/api/v2/torrents/resume")
|
||||
.Post()
|
||||
.AddFormParameter("hashes", hash);
|
||||
ProcessRequest(request, settings);
|
||||
}
|
||||
|
||||
public void SetForceStart(string hash, bool enabled, QBittorrentSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings).Resource("/api/v2/torrents/setForceStart")
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
using NzbDrone.Core.Annotations;
|
||||
|
||||
namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
{
|
||||
public enum QBittorrentState
|
||||
{
|
||||
[FieldOption(Label = "Started")]
|
||||
Start = 0,
|
||||
|
||||
[FieldOption(Label = "Force Started")]
|
||||
ForceStart = 1,
|
||||
Pause = 2
|
||||
|
||||
[FieldOption(Label = "Stopped")]
|
||||
Stop = 2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
@@ -22,8 +23,9 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
IHttpClient httpClient,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(httpClient, configService, diskProvider, logger)
|
||||
: base(httpClient, configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
_proxy = proxy;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
|
||||
namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
{
|
||||
@@ -15,8 +16,9 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
ISeedConfigProvider seedConfigProvider,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(proxy, torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger)
|
||||
: base(proxy, torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
@@ -20,8 +21,9 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
ISeedConfigProvider seedConfigProvider,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
_proxy = proxy;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Net;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
@@ -208,7 +209,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
|
||||
private void AuthenticateClient(HttpRequestBuilder requestBuilder, TransmissionSettings settings, bool reauthenticate = false)
|
||||
{
|
||||
var authKey = string.Format("{0}:{1}", requestBuilder.BaseUrl, settings.Password);
|
||||
var authKey = $"{requestBuilder.BaseUrl}:{settings.Password}";
|
||||
|
||||
var sessionId = _authSessionIDCache.Find(authKey);
|
||||
|
||||
@@ -220,24 +221,26 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
authLoginRequest.SuppressHttpError = true;
|
||||
|
||||
var response = _httpClient.Execute(authLoginRequest);
|
||||
if (response.StatusCode == HttpStatusCode.MovedPermanently)
|
||||
{
|
||||
var url = response.Headers.GetSingleValue("Location");
|
||||
|
||||
throw new DownloadClientException("Remote site redirected to " + url);
|
||||
}
|
||||
else if (response.StatusCode == HttpStatusCode.Conflict)
|
||||
switch (response.StatusCode)
|
||||
{
|
||||
sessionId = response.Headers.GetSingleValue("X-Transmission-Session-Id");
|
||||
case HttpStatusCode.MovedPermanently:
|
||||
var url = response.Headers.GetSingleValue("Location");
|
||||
|
||||
if (sessionId == null)
|
||||
{
|
||||
throw new DownloadClientException("Remote host did not return a Session Id.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new DownloadClientAuthenticationException("Failed to authenticate with Transmission.");
|
||||
throw new DownloadClientException("Remote site redirected to " + url);
|
||||
case HttpStatusCode.Forbidden:
|
||||
throw new DownloadClientException($"Failed to authenticate with Transmission. It may be necessary to add {BuildInfo.AppName}'s IP address to RPC whitelist.");
|
||||
case HttpStatusCode.Conflict:
|
||||
sessionId = response.Headers.GetSingleValue("X-Transmission-Session-Id");
|
||||
|
||||
if (sessionId == null)
|
||||
{
|
||||
throw new DownloadClientException("Remote host did not return a Session Id.");
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new DownloadClientAuthenticationException("Failed to authenticate with Transmission.");
|
||||
}
|
||||
|
||||
_logger.Debug("Transmission authentication succeeded.");
|
||||
|
||||
@@ -4,6 +4,7 @@ using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Download.Clients.Transmission;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
|
||||
namespace NzbDrone.Core.Download.Clients.Vuze
|
||||
{
|
||||
@@ -16,8 +17,9 @@ namespace NzbDrone.Core.Download.Clients.Vuze
|
||||
ISeedConfigProvider seedConfigProvider,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(proxy, torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger)
|
||||
: base(proxy, torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Download.Clients.rTorrent;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
@@ -27,8 +28,9 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
IRTorrentDirectoryValidator rTorrentDirectoryValidator,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
_proxy = proxy;
|
||||
_rTorrentDirectoryValidator = rTorrentDirectoryValidator;
|
||||
|
||||
@@ -7,7 +7,9 @@ using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||
@@ -21,8 +23,9 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||
ISeedConfigProvider seedConfigProvider,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, logger)
|
||||
: base(torrentFileInfoReader, seedConfigProvider, configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
_proxy = proxy;
|
||||
}
|
||||
@@ -72,6 +75,9 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||
}
|
||||
|
||||
public override string Name => "uTorrent";
|
||||
|
||||
public override ProviderMessage Message => new (_localizationService.GetLocalizedString("DownloadClientUTorrentProviderMessage"), ProviderMessageType.Warning);
|
||||
|
||||
public override bool SupportsCategories => true;
|
||||
|
||||
protected override void Test(List<ValidationFailure> failures)
|
||||
|
||||
@@ -8,6 +8,7 @@ using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
@@ -19,6 +20,7 @@ namespace NzbDrone.Core.Download
|
||||
{
|
||||
protected readonly IConfigService _configService;
|
||||
protected readonly IDiskProvider _diskProvider;
|
||||
protected readonly ILocalizationService _localizationService;
|
||||
protected readonly Logger _logger;
|
||||
|
||||
public abstract string Name { get; }
|
||||
@@ -40,10 +42,12 @@ namespace NzbDrone.Core.Download
|
||||
|
||||
protected DownloadClientBase(IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
{
|
||||
_configService = configService;
|
||||
_diskProvider = diskProvider;
|
||||
_localizationService = localizationService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace NzbDrone.Core.Download
|
||||
|
||||
foreach (var client in clients)
|
||||
{
|
||||
if (blockedClients.TryGetValue(client.Definition.Id, out var downloadClientStatus))
|
||||
if (blockedClients.TryGetValue(client.Definition.Id, out var downloadClientStatus) && downloadClientStatus.DisabledTill.HasValue)
|
||||
{
|
||||
_logger.Debug("Temporarily ignoring download client {0} till {1} due to recent failures.", client.Definition.Name, downloadClientStatus.DisabledTill.Value.ToLocalTime());
|
||||
continue;
|
||||
|
||||
@@ -8,6 +8,7 @@ using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
@@ -24,8 +25,9 @@ namespace NzbDrone.Core.Download
|
||||
ISeedConfigProvider seedConfigProvider,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(configService, diskProvider, logger)
|
||||
: base(configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
_torrentFileInfoReader = torrentFileInfoReader;
|
||||
_seedConfigProvider = seedConfigProvider;
|
||||
|
||||
@@ -5,6 +5,7 @@ using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
@@ -19,8 +20,9 @@ namespace NzbDrone.Core.Download
|
||||
protected UsenetClientBase(IHttpClient httpClient,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(configService, diskProvider, logger)
|
||||
: base(configService, diskProvider, localizationService, logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
private readonly IHttpRequestBuilderFactory _cloudRequestBuilder;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public SystemTimeCheck(IHttpClient client, IProwlarrCloudRequestBuilder cloudRequestBuilder, ILocalizationService localizationService, Logger logger)
|
||||
public SystemTimeCheck(IHttpClient client, IProwlarrCloudRequestBuilder cloudRequestBuilder, Logger logger, ILocalizationService localizationService)
|
||||
: base(localizationService)
|
||||
{
|
||||
_client = client;
|
||||
@@ -29,19 +29,26 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
|
||||
var request = _cloudRequestBuilder.Create()
|
||||
.Resource("/time")
|
||||
.Build();
|
||||
|
||||
var response = _client.Execute(request);
|
||||
var result = Json.Deserialize<ServiceTimeResponse>(response.Content);
|
||||
var systemTime = DateTime.UtcNow;
|
||||
|
||||
// +/- more than 1 day
|
||||
if (Math.Abs(result.DateTimeUtc.Subtract(systemTime).TotalDays) >= 1)
|
||||
try
|
||||
{
|
||||
_logger.Error("System time mismatch. SystemTime: {0} Expected Time: {1}. Update system time", systemTime, result.DateTimeUtc);
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Error, _localizationService.GetLocalizedString("SystemTimeCheckMessage"));
|
||||
var request = _cloudRequestBuilder.Create()
|
||||
.Resource("/time")
|
||||
.Build();
|
||||
|
||||
var response = _client.Execute(request);
|
||||
var result = Json.Deserialize<ServiceTimeResponse>(response.Content);
|
||||
var systemTime = DateTime.UtcNow;
|
||||
|
||||
// +/- more than 1 day
|
||||
if (Math.Abs(result.DateTimeUtc.Subtract(systemTime).TotalDays) >= 1)
|
||||
{
|
||||
_logger.Error("System time mismatch. SystemTime: {0} Expected Time: {1}. Update system time", systemTime, result.DateTimeUtc);
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Error, _localizationService.GetLocalizedString("SystemTimeHealthCheckMessage"), "#system-time-off");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Warn(e, "Unable to verify system time");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType());
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
if (latestAvailable != null)
|
||||
{
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Warning,
|
||||
BuildInfo.BuildDateTime.Before(DateTime.UtcNow.AddDays(-180)) ? HealthCheckResult.Error : HealthCheckResult.Warning,
|
||||
_localizationService.GetLocalizedString("UpdateAvailableHealthCheckMessage", new Dictionary<string, object>
|
||||
{
|
||||
{ "version", $"v{latestAvailable.Version}" }
|
||||
|
||||
@@ -224,7 +224,7 @@ namespace NzbDrone.Core.History
|
||||
|
||||
if (message.Release.PublishDate != DateTime.MinValue)
|
||||
{
|
||||
history.Data.Add("PublishedDate", message.Release.PublishDate.ToString("s") + "Z");
|
||||
history.Data.Add("PublishedDate", message.Release.PublishDate.ToUniversalTime().ToString("s") + "Z");
|
||||
}
|
||||
|
||||
_historyRepository.Insert(history);
|
||||
|
||||
@@ -232,7 +232,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
if (queryCats.Any() && searchCriteria is TvSearchCriteria { Season: > 0 })
|
||||
{
|
||||
// Avoid searching for specials if it's a non-zero season search
|
||||
queryCats.RemoveAll(cat => cat is "anime[tv_special]" or "anime[ova]" or "anime[ona]" or "anime[dvd_special]" or "anime[bd_special]");
|
||||
queryCats.RemoveAll(cat => cat is "anime[tv_special]" or "anime[ova]" or "anime[dvd_special]" or "anime[bd_special]");
|
||||
}
|
||||
|
||||
if (queryCats.Any())
|
||||
@@ -301,6 +301,8 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
};
|
||||
private static readonly HashSet<string> ExcludedFileExtensions = new (StringComparer.OrdinalIgnoreCase) { ".mka", ".mds", ".md5", ".nfo", ".sfv", ".ass", ".mks", ".srt", ".ssa", ".sup", ".jpeg", ".jpg", ".png", ".otf", ".ttf" };
|
||||
|
||||
private static readonly string[] PropertiesSeparator = { " | ", " / " };
|
||||
|
||||
private readonly AnimeBytesSettings _settings;
|
||||
|
||||
public AnimeBytesParser(AnimeBytesSettings settings)
|
||||
@@ -324,6 +326,11 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
var response = STJson.Deserialize<AnimeBytesResponse>(indexerResponse.Content);
|
||||
|
||||
if (response.Error.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
throw new IndexerException(indexerResponse, "Unexpected response from indexer request: {0}", response.Error);
|
||||
}
|
||||
|
||||
if (response.Matches == 0)
|
||||
{
|
||||
return releaseInfos.ToArray();
|
||||
@@ -393,38 +400,48 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var minimumSeedTime = 259200 + (int)(size / (int)Math.Pow(1024, 3) * 18000);
|
||||
|
||||
var propertyList = WebUtility.HtmlDecode(torrent.Property)
|
||||
.Split(new[] { " | ", " / " }, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)
|
||||
.Split(PropertiesSeparator, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)
|
||||
.ToList();
|
||||
|
||||
propertyList.RemoveAll(p => ExcludedProperties.Any(p.ContainsIgnoreCase));
|
||||
var properties = propertyList.ToHashSet();
|
||||
|
||||
if (torrent.Files.Any(f => f.FileName.ContainsIgnoreCase("Remux")))
|
||||
{
|
||||
var resolutionProperty = properties.FirstOrDefault(RemuxResolutions.ContainsIgnoreCase);
|
||||
|
||||
if (resolutionProperty.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
properties.Add($"{resolutionProperty} Remux");
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.Any(p => p.StartsWithIgnoreCase("M2TS")))
|
||||
if (properties.Any(p => p.StartsWith("M2TS", StringComparison.Ordinal)))
|
||||
{
|
||||
properties.Add("BR-DISK");
|
||||
}
|
||||
|
||||
if (_settings.ExcludeRaw &&
|
||||
properties.Any(p => p.StartsWithIgnoreCase("RAW") || p.Contains("BR-DISK")))
|
||||
var isBluRayDisk = properties.Any(p => p.Equals("RAW", StringComparison.Ordinal) || p.StartsWith("M2TS", StringComparison.Ordinal) || p.StartsWith("ISO", StringComparison.Ordinal));
|
||||
|
||||
if (_settings.ExcludeRaw && isBluRayDisk)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
properties = properties
|
||||
.Select(property =>
|
||||
{
|
||||
if (isBluRayDisk)
|
||||
{
|
||||
property = Regex.Replace(property, @"\b(H\.?265)\b", "HEVC", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
property = Regex.Replace(property, @"\b(H\.?264)\b", "AVC", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
if (torrent.Files.Any(f => f.FileName.ContainsIgnoreCase("Remux"))
|
||||
&& RemuxResolutions.ContainsIgnoreCase(property))
|
||||
{
|
||||
property += " Remux";
|
||||
}
|
||||
|
||||
return property;
|
||||
})
|
||||
.ToHashSet();
|
||||
|
||||
int? season = null;
|
||||
int? episode = null;
|
||||
|
||||
var releaseInfo = _settings.EnableSonarrCompatibility && categoryName == "Anime" ? "S01" : "";
|
||||
var editionTitle = torrent.EditionData.EditionTitle;
|
||||
var editionTitle = torrent.EditionData?.EditionTitle;
|
||||
|
||||
if (editionTitle.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
@@ -569,7 +586,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
if (_settings.UseFilenameForSingleEpisodes)
|
||||
{
|
||||
var files = torrent.Files;
|
||||
var files = torrent.Files.ToList();
|
||||
|
||||
if (files.Count > 1)
|
||||
{
|
||||
@@ -607,11 +624,13 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
}
|
||||
}
|
||||
|
||||
var useYearInTitle = year is > 0 && torrent.Files.Any(f => f.FileName.Contains(year.Value.ToString()));
|
||||
|
||||
foreach (var title in synonyms)
|
||||
{
|
||||
var releaseTitle = groupName is "Movie" or "Live Action Movie" ?
|
||||
$"{releaseGroup}{title} {year} {infoString}" :
|
||||
$"{releaseGroup}{title} {releaseInfo} {infoString}";
|
||||
$"{releaseGroup}{title}{(useYearInTitle ? $" {year}" : string.Empty)} {releaseInfo} {infoString}";
|
||||
|
||||
var guid = new Uri(details + "?nh=" + HashUtil.CalculateMd5(title));
|
||||
|
||||
@@ -650,7 +669,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var advancedSeasonRegex = new Regex(@"\b(?:(?<season>\d+)(?:st|nd|rd|th) Season|Season (?<season>\d+))\b", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
var seasonCharactersRegex = new Regex(@"(I{2,})$", RegexOptions.Compiled);
|
||||
var seasonNumberRegex = new Regex(@"\b(?<!Part[- ._])(?:S)?(?<season>[2-9])$", RegexOptions.Compiled);
|
||||
var seasonNumberRegex = new Regex(@"\b(?<!Part[- ._])(?<!\d[/])(?:S)?(?<season>[2-9])$", RegexOptions.Compiled);
|
||||
|
||||
foreach (var title in titles)
|
||||
{
|
||||
@@ -755,7 +774,9 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public int Matches { get; set; }
|
||||
|
||||
[JsonPropertyName("Groups")]
|
||||
public AnimeBytesGroup[] Groups { get; set; }
|
||||
public IReadOnlyCollection<AnimeBytesGroup> Groups { get; set; }
|
||||
|
||||
public string Error { get; set; }
|
||||
}
|
||||
|
||||
public class AnimeBytesGroup
|
||||
@@ -783,16 +804,16 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public string Image { get; set; }
|
||||
|
||||
[JsonPropertyName("SynonymnsV2")]
|
||||
public Dictionary<string, string> Synonymns { get; set; }
|
||||
public IReadOnlyDictionary<string, string> Synonymns { get; set; }
|
||||
|
||||
[JsonPropertyName("Description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[JsonPropertyName("Tags")]
|
||||
public List<string> Tags { get; set; }
|
||||
public IReadOnlyCollection<string> Tags { get; set; }
|
||||
|
||||
[JsonPropertyName("Torrents")]
|
||||
public List<AnimeBytesTorrent> Torrents { get; set; }
|
||||
public IReadOnlyCollection<AnimeBytesTorrent> Torrents { get; set; }
|
||||
}
|
||||
|
||||
public class AnimeBytesTorrent
|
||||
@@ -831,7 +852,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public int FileCount { get; set; }
|
||||
|
||||
[JsonPropertyName("FileList")]
|
||||
public List<AnimeBytesFile> Files { get; set; }
|
||||
public IReadOnlyCollection<AnimeBytesFile> Files { get; set; }
|
||||
|
||||
[JsonPropertyName("UploadTime")]
|
||||
public string UploadTime { get; set; }
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Html.Parser;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -44,46 +43,19 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
return new AnimeTorrentsParser(Settings, Capabilities.Categories);
|
||||
}
|
||||
|
||||
protected override async Task DoLogin()
|
||||
{
|
||||
UpdateCookies(null, null);
|
||||
|
||||
var loginUrl = Settings.BaseUrl + "login.php";
|
||||
|
||||
var loginPage = await ExecuteAuth(new HttpRequest(loginUrl));
|
||||
|
||||
var requestBuilder = new HttpRequestBuilder(loginUrl)
|
||||
{
|
||||
LogResponseContent = true,
|
||||
AllowAutoRedirect = true
|
||||
};
|
||||
|
||||
var authLoginRequest = requestBuilder
|
||||
.Post()
|
||||
.SetCookies(loginPage.GetCookies())
|
||||
.AddFormParameter("username", Settings.Username)
|
||||
.AddFormParameter("password", Settings.Password)
|
||||
.AddFormParameter("form", "login")
|
||||
.AddFormParameter("rememberme[]", "1")
|
||||
.SetHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||
.SetHeader("Referer", loginUrl)
|
||||
.Build();
|
||||
|
||||
var response = await ExecuteAuth(authLoginRequest);
|
||||
|
||||
if (response.Content == null || !response.Content.Contains("logout.php"))
|
||||
{
|
||||
throw new IndexerAuthException("AnimeTorrents authentication failed");
|
||||
}
|
||||
|
||||
UpdateCookies(response.GetCookies(), DateTime.Now.AddDays(30));
|
||||
|
||||
_logger.Debug("AnimeTorrents authentication succeeded");
|
||||
}
|
||||
|
||||
protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
|
||||
{
|
||||
return httpResponse.Content.Contains("Access Denied!") || httpResponse.Content.Contains("login.php");
|
||||
if (httpResponse.Content.Contains("Access Denied!") || httpResponse.Content.Contains("login.php"))
|
||||
{
|
||||
throw new IndexerAuthException("AnimeTorrents authentication with cookies failed.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override IDictionary<string, string> GetCookies()
|
||||
{
|
||||
return CookieUtil.CookieHeaderToDictionary(Settings.Cookie);
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
@@ -119,6 +91,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
caps.Categories.AddCategoryMapping(17, NewznabStandardCategory.BooksComics, "Doujinshi");
|
||||
caps.Categories.AddCategoryMapping(18, NewznabStandardCategory.BooksComics, "Doujinshi 18+");
|
||||
caps.Categories.AddCategoryMapping(19, NewznabStandardCategory.Audio, "OST");
|
||||
caps.Categories.AddCategoryMapping(20, NewznabStandardCategory.AudioAudiobook, "Audiobooks");
|
||||
|
||||
return caps;
|
||||
}
|
||||
@@ -291,7 +264,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var qTitleLink = row.QuerySelector("td:nth-of-type(2) a:nth-of-type(1)");
|
||||
var title = qTitleLink?.TextContent.Trim();
|
||||
|
||||
// If we search an get no results, we still get a table just with no info.
|
||||
// If we search and get no results, we still get a table just with no info.
|
||||
if (title.IsNullOrWhiteSpace())
|
||||
{
|
||||
break;
|
||||
@@ -306,6 +279,8 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
var connections = row.QuerySelector("td:nth-of-type(8)").TextContent.Trim().Split('/', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
|
||||
var seeders = ParseUtil.CoerceInt(connections[0]);
|
||||
var leechers = ParseUtil.CoerceInt(connections[1]);
|
||||
var grabs = ParseUtil.CoerceInt(connections[2]);
|
||||
|
||||
var categoryLink = row.QuerySelector("td:nth-of-type(1) a")?.GetAttribute("href") ?? string.Empty;
|
||||
var categoryId = ParseUtil.GetArgumentFromQueryString(categoryLink, "cat");
|
||||
@@ -327,17 +302,17 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
PublishDate = publishedDate,
|
||||
Size = ParseUtil.GetBytes(row.QuerySelector("td:nth-of-type(6)").TextContent.Trim()),
|
||||
Seeders = seeders,
|
||||
Peers = ParseUtil.CoerceInt(connections[1]) + seeders,
|
||||
Grabs = ParseUtil.CoerceInt(connections[2]),
|
||||
Peers = leechers + seeders,
|
||||
Grabs = grabs,
|
||||
DownloadVolumeFactor = downloadVolumeFactor,
|
||||
UploadVolumeFactor = 1,
|
||||
Genres = row.QuerySelectorAll("td:nth-of-type(2) a.tortags").Select(t => t.TextContent.Trim()).ToList()
|
||||
};
|
||||
|
||||
var uLFactorImg = row.QuerySelector("img[alt*=\"x Multiplier Torrent\"]");
|
||||
if (uLFactorImg != null)
|
||||
var uploadFactor = row.QuerySelector("img[alt*=\"x Multiplier Torrent\"]")?.GetAttribute("alt");
|
||||
if (uploadFactor != null)
|
||||
{
|
||||
release.UploadVolumeFactor = ParseUtil.CoerceDouble(uLFactorImg.GetAttribute("alt").Split('x')[0]);
|
||||
release.UploadVolumeFactor = ParseUtil.CoerceDouble(uploadFactor.Split('x')[0]);
|
||||
}
|
||||
|
||||
releaseInfos.Add(release);
|
||||
@@ -349,7 +324,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
public class AnimeTorrentsSettings : UserPassTorrentBaseSettings
|
||||
public class AnimeTorrentsSettings : CookieTorrentBaseSettings
|
||||
{
|
||||
public AnimeTorrentsSettings()
|
||||
{
|
||||
@@ -360,7 +335,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
[FieldDefinition(4, Label = "Freeleech Only", Type = FieldType.Checkbox, HelpText = "Show freeleech torrents only")]
|
||||
public bool FreeleechOnly { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Downloadable Only", Type = FieldType.Checkbox, HelpText = "Search downloadable torrents only (enable this only if your account class is Newbie)")]
|
||||
[FieldDefinition(5, Label = "Downloadable Only", Type = FieldType.Checkbox, HelpText = "Search downloadable torrents only (enable this only if your account class is Newbie)", Advanced = true)]
|
||||
public bool DownloadableOnly { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
return FilterReleasesByQuery(cleanReleases, searchCriteria).ToList();
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
private static IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
@@ -69,7 +69,8 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
},
|
||||
Flags = new List<IndexerFlag>
|
||||
{
|
||||
IndexerFlag.Internal
|
||||
IndexerFlag.Internal,
|
||||
IndexerFlag.Exclusive,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -91,7 +92,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
_capabilities = capabilities;
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(SearchCriteriaBase searchCriteria, string term, string imdbId = null, int tmdbId = 0)
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(SearchCriteriaBase searchCriteria, string searchTerm, string imdbId = null, int tmdbId = 0)
|
||||
{
|
||||
var body = new Dictionary<string, object>
|
||||
{
|
||||
@@ -128,9 +129,9 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
body.Add("tmdb_id", tmdbId);
|
||||
}
|
||||
|
||||
if (term.IsNotNullOrWhiteSpace())
|
||||
if (searchTerm.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
body.Add("search", term);
|
||||
body.Add("search", searchTerm.Trim());
|
||||
}
|
||||
|
||||
var cats = _capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories);
|
||||
@@ -198,7 +199,16 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria, searchCriteria.SanitizedTvSearchString, searchCriteria.FullImdbId));
|
||||
var searchTerm = searchCriteria.SanitizedTvSearchString;
|
||||
|
||||
if (searchCriteria.Season is > 0 &&
|
||||
searchCriteria.Episode.IsNotNullOrWhiteSpace() &&
|
||||
DateTime.TryParseExact($"{searchCriteria.Season} {searchCriteria.Episode}", "yyyy MM/dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var showDate))
|
||||
{
|
||||
searchTerm = $"{searchCriteria.SanitizedSearchTerm} {showDate.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)}";
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria, searchTerm, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -275,13 +285,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var details = row.InfoUrl;
|
||||
var link = row.DownloadLink;
|
||||
|
||||
var flags = new HashSet<IndexerFlag>();
|
||||
|
||||
if (row.Internal)
|
||||
{
|
||||
flags.Add(IndexerFlag.Internal);
|
||||
}
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Title = row.Name,
|
||||
@@ -291,7 +294,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
Guid = details,
|
||||
Categories = _categories.MapTrackerCatDescToNewznab(row.Category),
|
||||
PublishDate = DateTime.Parse(row.CreatedAt, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal),
|
||||
IndexerFlags = flags,
|
||||
IndexerFlags = GetIndexerFlags(row),
|
||||
Size = row.Size,
|
||||
Grabs = row.Grabs,
|
||||
Seeders = row.Seeders,
|
||||
@@ -301,6 +304,8 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
UploadVolumeFactor = 1,
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 172800, // 120 hours
|
||||
Languages = row.Audios?.Split(",", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List<string>(),
|
||||
Subs = row.Subtitles?.Split(",", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List<string>(),
|
||||
};
|
||||
|
||||
// BHD can return crazy values for tmdb
|
||||
@@ -319,6 +324,23 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static HashSet<IndexerFlag> GetIndexerFlags(BeyondHDTorrent item)
|
||||
{
|
||||
var flags = new HashSet<IndexerFlag>();
|
||||
|
||||
if (item.Internal)
|
||||
{
|
||||
flags.Add(IndexerFlag.Internal);
|
||||
}
|
||||
|
||||
if (item.Exclusive)
|
||||
{
|
||||
flags.Add(IndexerFlag.Exclusive);
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
@@ -326,8 +348,8 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
public BeyondHDSettingsValidator()
|
||||
{
|
||||
RuleFor(c => c.ApiKey).NotEmpty();
|
||||
RuleFor(c => c.RssKey).NotEmpty();
|
||||
RuleFor(c => c.ApiKey).NotEmpty().Length(32);
|
||||
RuleFor(c => c.RssKey).NotEmpty().Length(32);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,9 +472,13 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
[JsonPropertyName("times_completed")]
|
||||
public int Grabs { get; set; }
|
||||
|
||||
public int Seeders { get; set; }
|
||||
public int Leechers { get; set; }
|
||||
|
||||
public string Audios { get; set; }
|
||||
public string Subtitles { get; set; }
|
||||
|
||||
[JsonPropertyName("created_at")]
|
||||
public string CreatedAt { get; set; }
|
||||
|
||||
@@ -478,6 +504,8 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public bool Limited { get; set; }
|
||||
|
||||
public bool Exclusive { get; set; }
|
||||
|
||||
public bool Internal { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace NzbDrone.Core.Indexers.BroadcastheNet
|
||||
Guid = $"BTN-{torrent.TorrentID}",
|
||||
InfoUrl = $"{protocol}//broadcasthe.net/torrents.php?id={torrent.GroupID}&torrentid={torrent.TorrentID}",
|
||||
DownloadUrl = RegexProtocol.Replace(torrent.DownloadURL, protocol),
|
||||
Title = CleanReleaseName(torrent.ReleaseName),
|
||||
Title = GetTitle(torrent),
|
||||
Categories = _categories.MapTrackerCatToNewznab(torrent.Resolution),
|
||||
InfoHash = torrent.InfoHash,
|
||||
Size = torrent.Size,
|
||||
@@ -136,9 +136,17 @@ namespace NzbDrone.Core.Indexers.BroadcastheNet
|
||||
return releaseInfos;
|
||||
}
|
||||
|
||||
private string CleanReleaseName(string releaseName)
|
||||
private static string GetTitle(BroadcastheNetTorrent torrent)
|
||||
{
|
||||
return releaseName.Replace("\\", "");
|
||||
var releaseName = torrent.ReleaseName.Replace("\\", "");
|
||||
|
||||
if (torrent.Container.ToUpperInvariant() is "M2TS" or "ISO")
|
||||
{
|
||||
releaseName = Regex.Replace(releaseName, @"\b(H\.?265)\b", "HEVC", RegexOptions.Compiled);
|
||||
releaseName = Regex.Replace(releaseName, @"\b(H\.?264)\b", "AVC", RegexOptions.Compiled);
|
||||
}
|
||||
|
||||
return releaseName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ public class FileListParser : IParseIndexerResponse
|
||||
var url = new HttpUri(_settings.BaseUrl)
|
||||
.CombinePath("/download.php")
|
||||
.AddQueryParam("id", torrentId.ToString())
|
||||
.AddQueryParam("passkey", _settings.Passkey);
|
||||
.AddQueryParam("passkey", _settings.Passkey.Trim());
|
||||
|
||||
return url.FullUri;
|
||||
}
|
||||
|
||||
@@ -95,3 +95,8 @@ public class GazelleIndexResponse
|
||||
public string Authkey { get; set; }
|
||||
public string Passkey { get; set; }
|
||||
}
|
||||
|
||||
public class GazelleErrorResponse
|
||||
{
|
||||
public string Error { get; init; }
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
@@ -32,7 +33,9 @@ public class GazelleParser : IParseIndexerResponse
|
||||
// Remove cookie cache
|
||||
CookiesUpdater(null, null);
|
||||
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request");
|
||||
STJson.TryDeserialize<GazelleErrorResponse>(indexerResponse.Content, out var errorResponse);
|
||||
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request: {errorResponse?.Error ?? "Check the logs for more information."}");
|
||||
}
|
||||
|
||||
if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value))
|
||||
|
||||
@@ -7,6 +7,7 @@ using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Definitions.Gazelle;
|
||||
@@ -148,7 +149,9 @@ public class GreatPosterWallParser : GazelleParser
|
||||
throw new IndexerException(indexerResponse, $"Redirected to {indexerResponse.HttpResponse.RedirectUrl} from indexer request");
|
||||
}
|
||||
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request");
|
||||
STJson.TryDeserialize<GazelleErrorResponse>(indexerResponse.Content, out var errorResponse);
|
||||
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request: {errorResponse?.Error ?? "Check the logs for more information."}");
|
||||
}
|
||||
|
||||
if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value))
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace NzbDrone.Core.Indexers.Definitions.HDBits
|
||||
return new HDBitsParser(Settings, Capabilities.Categories);
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
private static IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
@@ -43,6 +43,11 @@ namespace NzbDrone.Core.Indexers.Definitions.HDBits
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
Flags = new List<IndexerFlag>
|
||||
{
|
||||
IndexerFlag.Internal,
|
||||
IndexerFlag.Exclusive,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -85,6 +85,9 @@ namespace NzbDrone.Core.Indexers.Definitions.HDBits
|
||||
[JsonProperty(PropertyName = "type_origin")]
|
||||
public int TypeOrigin { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "type_exclusive")]
|
||||
public int TypeExclusive { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "imdb")]
|
||||
public ImdbInfo ImdbInfo { get; set; }
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions.HDBits
|
||||
{
|
||||
public class HDBitsInfo : TorrentInfo
|
||||
{
|
||||
public bool? Internal { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -62,16 +62,8 @@ namespace NzbDrone.Core.Indexers.Definitions.HDBits
|
||||
}
|
||||
|
||||
var id = result.Id;
|
||||
var internalRelease = result.TypeOrigin == 1;
|
||||
|
||||
var flags = new HashSet<IndexerFlag>();
|
||||
|
||||
if (internalRelease)
|
||||
{
|
||||
flags.Add(IndexerFlag.Internal);
|
||||
}
|
||||
|
||||
releaseInfos.Add(new HDBitsInfo
|
||||
releaseInfos.Add(new TorrentInfo
|
||||
{
|
||||
Guid = $"HDBits-{id}",
|
||||
Title = GetTitle(result),
|
||||
@@ -85,28 +77,43 @@ namespace NzbDrone.Core.Indexers.Definitions.HDBits
|
||||
Files = (int)result.NumFiles,
|
||||
Peers = result.Leechers + result.Seeders,
|
||||
PublishDate = result.Added.ToUniversalTime(),
|
||||
Internal = internalRelease,
|
||||
Year = result.ImdbInfo?.Year ?? 0,
|
||||
ImdbId = result.ImdbInfo?.Id ?? 0,
|
||||
TvdbId = result.TvdbInfo?.Id ?? 0,
|
||||
DownloadVolumeFactor = GetDownloadVolumeFactor(result),
|
||||
UploadVolumeFactor = GetUploadVolumeFactor(result),
|
||||
IndexerFlags = flags
|
||||
IndexerFlags = GetIndexerFlags(result)
|
||||
});
|
||||
}
|
||||
|
||||
return releaseInfos.ToArray();
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
|
||||
private string GetTitle(TorrentQueryResponse item)
|
||||
{
|
||||
return _settings.UseFilenames && item.FileName.IsNotNullOrWhiteSpace()
|
||||
// Use release name for XXX content and full discs
|
||||
return item.TypeCategory != 7 && item.TypeMedium != 1 && _settings.UseFilenames && item.FileName.IsNotNullOrWhiteSpace()
|
||||
? item.FileName.Replace(".torrent", "", StringComparison.InvariantCultureIgnoreCase)
|
||||
: item.Name;
|
||||
}
|
||||
|
||||
private static HashSet<IndexerFlag> GetIndexerFlags(TorrentQueryResponse item)
|
||||
{
|
||||
var flags = new HashSet<IndexerFlag>();
|
||||
|
||||
if (item.TypeOrigin == 1)
|
||||
{
|
||||
flags.Add(IndexerFlag.Internal);
|
||||
}
|
||||
|
||||
if (item.TypeExclusive == 1)
|
||||
{
|
||||
flags.Add(IndexerFlag.Exclusive);
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
private double GetDownloadVolumeFactor(TorrentQueryResponse item)
|
||||
{
|
||||
if (item.FreeLeech == "yes")
|
||||
@@ -153,5 +160,7 @@ namespace NzbDrone.Core.Indexers.Definitions.HDBits
|
||||
|
||||
return url.FullUri;
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user