Compare commits
1274 Commits
v0.2
...
new-exclus
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
793cf22c3b | ||
|
|
76cabb4927 | ||
|
|
5b83d09d5e | ||
|
|
82eadcffaa | ||
|
|
1ebb71db59 | ||
|
|
662b3894c2 | ||
|
|
a1c21af9b5 | ||
|
|
67fca87c44 | ||
|
|
6ee2780370 | ||
|
|
0086e2699e | ||
|
|
37c9701237 | ||
|
|
6d452d8479 | ||
|
|
e82f0c01d8 | ||
|
|
ee456c3291 | ||
|
|
9986f0119b | ||
|
|
5d4da26195 | ||
|
|
5ed448c930 | ||
|
|
9263e31b7b | ||
|
|
516122c6f3 | ||
|
|
256a50abab | ||
|
|
315929bc5e | ||
|
|
4a681601b2 | ||
|
|
ab3b5bdf8b | ||
|
|
e52288bd67 | ||
|
|
f2f26d88b9 | ||
|
|
27354507cb | ||
|
|
b7aa1df219 | ||
|
|
2d7942d69c | ||
|
|
0a8dd85856 | ||
|
|
f917d0e9bc | ||
|
|
024e4df99c | ||
|
|
49fa402c55 | ||
|
|
02c95658c4 | ||
|
|
3bc4231640 | ||
|
|
a9a0d47f9f | ||
|
|
0dd05b2dac | ||
|
|
3986433884 | ||
|
|
f637976530 | ||
|
|
603d26bb5f | ||
|
|
6b41ad7442 | ||
|
|
4c049ac3d9 | ||
|
|
84df3e8b5b | ||
|
|
0ff76e14bb | ||
|
|
5433c6364c | ||
|
|
038e3d5c44 | ||
|
|
71b126024b | ||
|
|
76565d4ab5 | ||
|
|
28c15bc425 | ||
|
|
aeda4cba32 | ||
|
|
a826c1dc25 | ||
|
|
00022fd206 | ||
|
|
0647663f46 | ||
|
|
b83bfb045b | ||
|
|
cfbac482e5 | ||
|
|
b43b35a295 | ||
|
|
e75d52de0e | ||
|
|
f850e75d4e | ||
|
|
6c9e4994d8 | ||
|
|
708a2e31d5 | ||
|
|
37c1b5b28c | ||
|
|
e28bea14b3 | ||
|
|
2823099237 | ||
|
|
53eeee8b91 | ||
|
|
e880eb0e00 | ||
|
|
561f84adff | ||
|
|
4fc6a14d1b | ||
|
|
b8d5a0b6a2 | ||
|
|
73b0a461d3 | ||
|
|
d86402efb1 | ||
|
|
1c892d7357 | ||
|
|
f7e21ec2a4 | ||
|
|
1d0771c9a4 | ||
|
|
cc384d9297 | ||
|
|
7898100d95 | ||
|
|
4b279e87cf | ||
|
|
3d5570dfd9 | ||
|
|
269462e0a2 | ||
|
|
5799b3dc47 | ||
|
|
dfbbb7d9bd | ||
|
|
6faa484d4e | ||
|
|
86363d5bf1 | ||
|
|
30c51ec4f3 | ||
|
|
a66b2cf416 | ||
|
|
37197150be | ||
|
|
c8bbd21615 | ||
|
|
fdf2d1c9b3 | ||
|
|
eb4cd9633a | ||
|
|
3fa17bf7f6 | ||
|
|
22e9dff76b | ||
|
|
7d31eb1f55 | ||
|
|
4ec71538b9 | ||
|
|
295b975046 | ||
|
|
0198c7a3b1 | ||
|
|
088ffc34df | ||
|
|
1740ad337c | ||
|
|
a6758e4bf4 | ||
|
|
2161f08140 | ||
|
|
1a92372506 | ||
|
|
5bd23be133 | ||
|
|
eca816db86 | ||
|
|
c4f19a813d | ||
|
|
24cee7e4fe | ||
|
|
22531294be | ||
|
|
464d92bb70 | ||
|
|
8ee16b81ec | ||
|
|
a7a1d48e0d | ||
|
|
a070279993 | ||
|
|
95918c4053 | ||
|
|
646b86f8c9 | ||
|
|
28835a1857 | ||
|
|
6b22481a00 | ||
|
|
aef8a8fd04 | ||
|
|
a1e69c3c2b | ||
|
|
995d257d3d | ||
|
|
cf804f7dac | ||
|
|
081fe64bff | ||
|
|
9075fdc1c1 | ||
|
|
5db1faad0b | ||
|
|
57f3805763 | ||
|
|
6bdd24e62d | ||
|
|
d5ec2914e2 | ||
|
|
d8a0aac9c3 | ||
|
|
987ed357d5 | ||
|
|
9044976393 | ||
|
|
b53def3da5 | ||
|
|
2ecb988c6a | ||
|
|
41cf722ab5 | ||
|
|
dbaccc5f91 | ||
|
|
9864d555f2 | ||
|
|
b4abfaa695 | ||
|
|
7f814a3cb9 | ||
|
|
870a39278c | ||
|
|
7e5d5fe29e | ||
|
|
6d4543f1df | ||
|
|
98ebc8ff97 | ||
|
|
b8743bd73e | ||
|
|
4ea9ded0de | ||
|
|
2ee77aa0a4 | ||
|
|
c7c97e508d | ||
|
|
283c26222b | ||
|
|
fb0fa53304 | ||
|
|
ca27a80b15 | ||
|
|
ed289ddbdd | ||
|
|
5a36980996 | ||
|
|
de28266071 | ||
|
|
599263f998 | ||
|
|
fd711a9bf3 | ||
|
|
ad5a90f034 | ||
|
|
2ad1cfec42 | ||
|
|
6fdf06a882 | ||
|
|
4e0f027a91 | ||
|
|
e56a784e4c | ||
|
|
1898114d8c | ||
|
|
d6d740a0d2 | ||
|
|
9472af8f83 | ||
|
|
01f0f9d3bb | ||
|
|
9e79e91d39 | ||
|
|
db05f0464a | ||
|
|
ff3fd0bb1e | ||
|
|
0100819ae4 | ||
|
|
dd3218588f | ||
|
|
e086b760c1 | ||
|
|
f883f96325 | ||
|
|
5abffcab54 | ||
|
|
116a3b2de1 | ||
|
|
9521b6883d | ||
|
|
affebb3552 | ||
|
|
65b0fcffbe | ||
|
|
4c0a906bfa | ||
|
|
7e0815b3b4 | ||
|
|
73cc67f71e | ||
|
|
f089d11001 | ||
|
|
7791b4038e | ||
|
|
77b2945e84 | ||
|
|
02845c72d3 | ||
|
|
587e6b570c | ||
|
|
7d9f625080 | ||
|
|
480722df71 | ||
|
|
07596a6f8d | ||
|
|
3fbf591eb9 | ||
|
|
9a46d5165c | ||
|
|
4fafdcabb7 | ||
|
|
69071768de | ||
|
|
4be83a3367 | ||
|
|
7ddaca06a6 | ||
|
|
54dc22a763 | ||
|
|
29bc38423b | ||
|
|
da720fdf4c | ||
|
|
2f311452da | ||
|
|
d615007ff1 | ||
|
|
d9ed97f63a | ||
|
|
ecf6dccb35 | ||
|
|
d31b1e1e9b | ||
|
|
7019c8587b | ||
|
|
df96203914 | ||
|
|
bee8da5c8a | ||
|
|
c42b1d6265 | ||
|
|
5aa45dbc85 | ||
|
|
5d64417b7b | ||
|
|
c51b08e26c | ||
|
|
a2e8d1d5d6 | ||
|
|
ea37bc2cb7 | ||
|
|
bba2b01980 | ||
|
|
b43732b343 | ||
|
|
764d456d49 | ||
|
|
9a2cee3104 | ||
|
|
4311048c08 | ||
|
|
5b3a09716b | ||
|
|
ecbdd58f42 | ||
|
|
2014ff6610 | ||
|
|
c3a89c46f5 | ||
|
|
e014826b17 | ||
|
|
f8c3947bad | ||
|
|
8598cfcac9 | ||
|
|
18982cd054 | ||
|
|
03715d9b6a | ||
|
|
fea479e7e7 | ||
|
|
acd4e9b8aa | ||
|
|
2a84c4737b | ||
|
|
bc4bb772ec | ||
|
|
6179540d27 | ||
|
|
f7e208afe1 | ||
|
|
bc83e902cc | ||
|
|
2b7af5d99d | ||
|
|
1971a9dd05 | ||
|
|
328c3de7c1 | ||
|
|
7c8ac30077 | ||
|
|
f286f99bb6 | ||
|
|
914d8f9937 | ||
|
|
b787f3b2d1 | ||
|
|
7ec043afde | ||
|
|
c3223ea8e7 | ||
|
|
87bf6b5977 | ||
|
|
7ddb8ecc37 | ||
|
|
9b1dc2d21d | ||
|
|
ff3e53a71b | ||
|
|
35532b6789 | ||
|
|
026c34c3ba | ||
|
|
7b60612ef3 | ||
|
|
38855967d6 | ||
|
|
67d2765d0c | ||
|
|
c1b6917afe | ||
|
|
95a30b50fa | ||
|
|
17b93bcc57 | ||
|
|
16321377fc | ||
|
|
e6368c3095 | ||
|
|
a4869db050 | ||
|
|
bdecc48e34 | ||
|
|
916c10197e | ||
|
|
3fe06ab3ac | ||
|
|
4f52a3e10f | ||
|
|
b466530e08 | ||
|
|
f1727b0960 | ||
|
|
579a86a708 | ||
|
|
b6f2afb212 | ||
|
|
3a7b48825d | ||
|
|
0d7bf98efb | ||
|
|
ded8c9ea3e | ||
|
|
aeb7d79101 | ||
|
|
e45f0ad5fc | ||
|
|
87bf010c67 | ||
|
|
50142eea64 | ||
|
|
c26d25fdfb | ||
|
|
bd85f78994 | ||
|
|
1ddb593ddd | ||
|
|
21899e7a43 | ||
|
|
04ea182abb | ||
|
|
e033ce1eff | ||
|
|
261e598c99 | ||
|
|
38c7c60ad7 | ||
|
|
3548433a10 | ||
|
|
361ffe353d | ||
|
|
54bcf9eed3 | ||
|
|
f6364fec1c | ||
|
|
7f8ebf41d6 | ||
|
|
a9ee3d5bc5 | ||
|
|
1dfe6eb6ed | ||
|
|
92a0305740 | ||
|
|
e610b74745 | ||
|
|
3f0903d8ab | ||
|
|
d474b487ec | ||
|
|
cdf1501cbd | ||
|
|
613f9a9cd8 | ||
|
|
eee75f630e | ||
|
|
cae169f4b8 | ||
|
|
4f3948d36a | ||
|
|
df18be2a4d | ||
|
|
be38ca4728 | ||
|
|
84e769b14e | ||
|
|
0800029713 | ||
|
|
ab7bb610a6 | ||
|
|
9a0f6cf931 | ||
|
|
658f6922c2 | ||
|
|
30fe718dcf | ||
|
|
5f8b953f6a | ||
|
|
a28c5675ef | ||
|
|
df197d2e16 | ||
|
|
4d193b2279 | ||
|
|
eff03a7d2c | ||
|
|
2cbeefba6e | ||
|
|
0d7b48d48a | ||
|
|
70c057f20f | ||
|
|
b342dc7ca0 | ||
|
|
d8ff3a62ea | ||
|
|
363ff7df3c | ||
|
|
fa3914c36a | ||
|
|
aba9cedb78 | ||
|
|
197f064fd5 | ||
|
|
acd167e3ff | ||
|
|
60ff140e7f | ||
|
|
fcea6d23b0 | ||
|
|
1250c3db92 | ||
|
|
ee225d9d85 | ||
|
|
ec752f8ab3 | ||
|
|
a8e31896e3 | ||
|
|
1daa1f9daf | ||
|
|
9d79a28655 | ||
|
|
665a5c89c2 | ||
|
|
f7bebbaaeb | ||
|
|
04b84e4931 | ||
|
|
e4d033d0a7 | ||
|
|
9c85482b9c | ||
|
|
ac7f6f8868 | ||
|
|
56184905a9 | ||
|
|
5a1b82e195 | ||
|
|
d92895381c | ||
|
|
f9f243a814 | ||
|
|
3c5e56919f | ||
|
|
05e8de2b0a | ||
|
|
e2c0d83f20 | ||
|
|
c96f29f1e8 | ||
|
|
c8de61df19 | ||
|
|
7c5b2a299d | ||
|
|
76804dfefb | ||
|
|
ed2568187e | ||
|
|
f1bb8eeb8e | ||
|
|
547c044dc6 | ||
|
|
0ef9d28a73 | ||
|
|
ed76709b7f | ||
|
|
154092b921 | ||
|
|
28177eb042 | ||
|
|
a6d509c871 | ||
|
|
69f4f1c168 | ||
|
|
79a96bb43f | ||
|
|
39a1dbf1d1 | ||
|
|
71ad2ec794 | ||
|
|
77036ac3ef | ||
|
|
863252d7e9 | ||
|
|
00f631c623 | ||
|
|
431a3f6f8f | ||
|
|
3aafc02687 | ||
|
|
c96ffafe50 | ||
|
|
487546d6f3 | ||
|
|
9f925a5dbd | ||
|
|
d6a51f875c | ||
|
|
2575e7d96a | ||
|
|
50b3a83cda | ||
|
|
0f33c9b3bd | ||
|
|
814283cfc3 | ||
|
|
23e1341ef3 | ||
|
|
5413c9357c | ||
|
|
eb299bfb1f | ||
|
|
d3697c0339 | ||
|
|
9201b8e329 | ||
|
|
f34ff4515a | ||
|
|
5eeb42c3f6 | ||
|
|
2d121e9857 | ||
|
|
3d25b99f63 | ||
|
|
30956edf52 | ||
|
|
c940a03847 | ||
|
|
21bc18dd2a | ||
|
|
52da43aac4 | ||
|
|
29ea7aab9d | ||
|
|
236cdc9c2a | ||
|
|
4726d9daad | ||
|
|
74c144b9c6 | ||
|
|
ff52b15154 | ||
|
|
18607c8b7b | ||
|
|
d41eed2e79 | ||
|
|
7f1761bec5 | ||
|
|
cff8dd5856 | ||
|
|
17cbeb7cab | ||
|
|
f952b7188d | ||
|
|
16cd7dd836 | ||
|
|
1412125dfd | ||
|
|
6455117dbb | ||
|
|
a29de48e64 | ||
|
|
43d6b404f5 | ||
|
|
e3fb000226 | ||
|
|
9cb3ff238b | ||
|
|
97980fe139 | ||
|
|
ec15885680 | ||
|
|
695297435e | ||
|
|
1a9d7c3c28 | ||
|
|
94ec28ac1e | ||
|
|
a4155e0416 | ||
|
|
0884361df2 | ||
|
|
09bea395ac | ||
|
|
25d4704e6d | ||
|
|
f0d8c7c187 | ||
|
|
3766e0eba9 | ||
|
|
f39824cb9c | ||
|
|
ef80314219 | ||
|
|
e232fa2c9c | ||
|
|
41fc244bda | ||
|
|
0bb5dfc3d2 | ||
|
|
67b4182f4d | ||
|
|
27844d9f5c | ||
|
|
9b052101ab | ||
|
|
b2b0efe4ed | ||
|
|
b25e42c456 | ||
|
|
5c7e28a280 | ||
|
|
bd9a93eceb | ||
|
|
df75027e3c | ||
|
|
c950006db5 | ||
|
|
fabf9ba0e8 | ||
|
|
c07b370569 | ||
|
|
ff7e929387 | ||
|
|
da1210c6a6 | ||
|
|
2a1ebe54b3 | ||
|
|
65b8be16dd | ||
|
|
84c79924b7 | ||
|
|
62d2c8e1f3 | ||
|
|
e6c92c05ec | ||
|
|
bb02b9d83a | ||
|
|
7cf7baf637 | ||
|
|
f8908936d3 | ||
|
|
c3307570cc | ||
|
|
011a80b237 | ||
|
|
82b546999a | ||
|
|
71492d4c6f | ||
|
|
0f6fca8340 | ||
|
|
8ddaf348f1 | ||
|
|
55e6605ed8 | ||
|
|
d736635ae4 | ||
|
|
a977c5148e | ||
|
|
06d200f078 | ||
|
|
079b7a7cb7 | ||
|
|
c11928ffb8 | ||
|
|
b7a617bc4e | ||
|
|
d2949b31ab | ||
|
|
c9324bc357 | ||
|
|
ca8f4acdaa | ||
|
|
7eedb7fbec | ||
|
|
71e715f954 | ||
|
|
b92414d657 | ||
|
|
85fda91604 | ||
|
|
7722c50603 | ||
|
|
75eb4e8519 | ||
|
|
84134eb6ce | ||
|
|
765ca89810 | ||
|
|
9ce58be385 | ||
|
|
33cd699eac | ||
|
|
c34a5c0c3d | ||
|
|
80ae55920b | ||
|
|
eb0be34924 | ||
|
|
483c2ae724 | ||
|
|
e2165eb51b | ||
|
|
6d4be67e36 | ||
|
|
b6c75e7e1b | ||
|
|
c84a9d6612 | ||
|
|
c04f26b7f1 | ||
|
|
16c912ffea | ||
|
|
8871864bc0 | ||
|
|
ea7c08d219 | ||
|
|
86a75e2641 | ||
|
|
b077d1c6cf | ||
|
|
3a07720587 | ||
|
|
fabf758230 | ||
|
|
67ea64fa5e | ||
|
|
7a19d0a88c | ||
|
|
e88b794ce0 | ||
|
|
0a414f37dc | ||
|
|
5b38edfff9 | ||
|
|
09976c6afe | ||
|
|
bca7a38003 | ||
|
|
1934a248f8 | ||
|
|
bed4604e62 | ||
|
|
deabc3aa9a | ||
|
|
b5f173b018 | ||
|
|
69bd3f7d6d | ||
|
|
947ec567ba | ||
|
|
12414e0bde | ||
|
|
012cb66b45 | ||
|
|
8ce223d01a | ||
|
|
158a06adbf | ||
|
|
5765961c3a | ||
|
|
539f495dbe | ||
|
|
23349c1063 | ||
|
|
c2317e3567 | ||
|
|
26409c9d36 | ||
|
|
8799da55d3 | ||
|
|
fa60c28e9c | ||
|
|
175b3de373 | ||
|
|
957c7a9266 | ||
|
|
a4cd52d931 | ||
|
|
687a055a61 | ||
|
|
54299694ef | ||
|
|
bd06c756b7 | ||
|
|
b69ed9fd11 | ||
|
|
8e1cb149fa | ||
|
|
ef879c3a91 | ||
|
|
9603f9fdb5 | ||
|
|
fc7091ef0c | ||
|
|
d37fde5f06 | ||
|
|
575dbd18d9 | ||
|
|
2a4f959a87 | ||
|
|
b7222c2c55 | ||
|
|
47404783c2 | ||
|
|
29f2543d57 | ||
|
|
f8070f0a8e | ||
|
|
818b330ece | ||
|
|
4fa1fbe23c | ||
|
|
2919b282f6 | ||
|
|
2390ab98fa | ||
|
|
1c85bd96f8 | ||
|
|
df8023bf02 | ||
|
|
9109280ae2 | ||
|
|
a59ea1f8d2 | ||
|
|
60ebafc940 | ||
|
|
ac1956f5ad | ||
|
|
2242f2aba3 | ||
|
|
8010030ca1 | ||
|
|
4ac18dd023 | ||
|
|
c75362ecc8 | ||
|
|
bf061f2a96 | ||
|
|
c6367112c2 | ||
|
|
074b49fa8c | ||
|
|
bfc0361784 | ||
|
|
200c7487b9 | ||
|
|
ffff528ccb | ||
|
|
5389c86cde | ||
|
|
3a0aa51cbf | ||
|
|
5eb7a83996 | ||
|
|
bd5bb2a63c | ||
|
|
d7e0625be7 | ||
|
|
a22c13bfa3 | ||
|
|
3f39c8d1bd | ||
|
|
1dbb664ef6 | ||
|
|
0b7067cf9c | ||
|
|
d8dc35913d | ||
|
|
027a388157 | ||
|
|
a552389ee8 | ||
|
|
f8f58c2eda | ||
|
|
113f5a9bfe | ||
|
|
9d913899ca | ||
|
|
c9a9babdf3 | ||
|
|
6be1ae0120 | ||
|
|
ac79c51196 | ||
|
|
9bf50d4493 | ||
|
|
aa6c8f493e | ||
|
|
135251ec31 | ||
|
|
c51fe81f41 | ||
|
|
7d22696b1f | ||
|
|
d791f3f67f | ||
|
|
74539d2025 | ||
|
|
075f024cec | ||
|
|
3a50152b21 | ||
|
|
88bda6bcb6 | ||
|
|
10322a1867 | ||
|
|
965ed041ae | ||
|
|
2b5dc59a36 | ||
|
|
b82d636a8c | ||
|
|
5982731ef7 | ||
|
|
b97e6977fb | ||
|
|
98c4e63309 | ||
|
|
f37c7a9748 | ||
|
|
a08648272c | ||
|
|
0142c45210 | ||
|
|
f073f0c35c | ||
|
|
b7cf6f49d0 | ||
|
|
347cd5982a | ||
|
|
0a589c529f | ||
|
|
f29e7557dd | ||
|
|
7f201c6677 | ||
|
|
a77d43bd43 | ||
|
|
e18e074ee9 | ||
|
|
4f22d135d6 | ||
|
|
8f791853ad | ||
|
|
e870fd5215 | ||
|
|
6706138fa0 | ||
|
|
5ee2b10c2c | ||
|
|
ccb206aed1 | ||
|
|
cdd653bea9 | ||
|
|
afada848c8 | ||
|
|
e09ca145d1 | ||
|
|
a8deaf85c0 | ||
|
|
f069801eba | ||
|
|
5edbe4200b | ||
|
|
5b6bef36f2 | ||
|
|
79c35fabfa | ||
|
|
ee9d35e55f | ||
|
|
c0bb1392e2 | ||
|
|
8c84047a56 | ||
|
|
0e563db10b | ||
|
|
3a7b1741d9 | ||
|
|
8687dbda1d | ||
|
|
c896833607 | ||
|
|
3a7c4b2cfe | ||
|
|
a946d5886c | ||
|
|
dfba9e9b4d | ||
|
|
28e6e4ed7b | ||
|
|
22d0f9ffef | ||
|
|
7120a20984 | ||
|
|
40b5702ca6 | ||
|
|
3c7f80612d | ||
|
|
2328b384e2 | ||
|
|
566fa8b132 | ||
|
|
8d57c5497a | ||
|
|
62b3ed5d48 | ||
|
|
6802bfc736 | ||
|
|
2d59192a9e | ||
|
|
3e3b2a7784 | ||
|
|
9c3b4e3025 | ||
|
|
b9c59e5482 | ||
|
|
1849ce4190 | ||
|
|
021e7b8163 | ||
|
|
4b25ef6deb | ||
|
|
30def1f53a | ||
|
|
57961df1df | ||
|
|
40b630ef10 | ||
|
|
e59257f457 | ||
|
|
a6c2b2e039 | ||
|
|
51de8f16fb | ||
|
|
5f5391db47 | ||
|
|
73eba0f95d | ||
|
|
1ef31e8094 | ||
|
|
b1fd924188 | ||
|
|
d10e60587b | ||
|
|
88879e0db6 | ||
|
|
2a9160f870 | ||
|
|
64382e13a4 | ||
|
|
232682f109 | ||
|
|
d07dd33a9e | ||
|
|
7adb358d1c | ||
|
|
913037c45e | ||
|
|
b00326424f | ||
|
|
67ff8d39da | ||
|
|
13f3d0292c | ||
|
|
9ca291ecaf | ||
|
|
1fcb927b3b | ||
|
|
be9b2d9ebb | ||
|
|
97ff509025 | ||
|
|
c6c4eb0129 | ||
|
|
aefa4aa20b | ||
|
|
1209e3cefb | ||
|
|
70a2da0f74 | ||
|
|
5de1f4563a | ||
|
|
8112ca5a43 | ||
|
|
f2a34e5eda | ||
|
|
6ecffc9bed | ||
|
|
f0be9994b1 | ||
|
|
9ede227178 | ||
|
|
f59b391b28 | ||
|
|
de2ebba363 | ||
|
|
2a3d22038f | ||
|
|
baa6972a62 | ||
|
|
7f1497974f | ||
|
|
d01c0afa56 | ||
|
|
874b1bd17c | ||
|
|
f00f4d0c2c | ||
|
|
b997bf21a5 | ||
|
|
62de7b63bd | ||
|
|
3e5626f894 | ||
|
|
ba6ba06d9b | ||
|
|
1d6a7a1843 | ||
|
|
0479bb9cc3 | ||
|
|
4951a97984 | ||
|
|
70601faa4f | ||
|
|
846b3324e5 | ||
|
|
5761ce640b | ||
|
|
789e47dfff | ||
|
|
0080e6b685 | ||
|
|
250cc09239 | ||
|
|
00b9118dbd | ||
|
|
d3d3117bf3 | ||
|
|
50f84101e0 | ||
|
|
2bdd806565 | ||
|
|
4a5012f98e | ||
|
|
c8a2af867e | ||
|
|
c64c2d9f27 | ||
|
|
9bdaea4a1b | ||
|
|
e8e852100c | ||
|
|
83679214b3 | ||
|
|
10074ae994 | ||
|
|
78e83488b9 | ||
|
|
e52005fa35 | ||
|
|
6463befc79 | ||
|
|
d03a6486d3 | ||
|
|
5c9b85972d | ||
|
|
32a6c9fe2a | ||
|
|
ebc72bfba9 | ||
|
|
c0b80696bc | ||
|
|
78c7372a0d | ||
|
|
6ad3653c04 | ||
|
|
2c6b13dd11 | ||
|
|
93d27c70c4 | ||
|
|
8b06df1b1a | ||
|
|
daee51a8ab | ||
|
|
5e0ba314d2 | ||
|
|
4606503818 | ||
|
|
942d239092 | ||
|
|
a19bcf9683 | ||
|
|
95f66117e4 | ||
|
|
f332f8d7cd | ||
|
|
d6f15af6b6 | ||
|
|
ce9fc48910 | ||
|
|
c988151b5d | ||
|
|
cd50767273 | ||
|
|
e46c171d54 | ||
|
|
dbc784e94b | ||
|
|
ac37285be6 | ||
|
|
13bd9535cc | ||
|
|
cb97f90b9e | ||
|
|
91ed949a47 | ||
|
|
f53a13ab41 | ||
|
|
4d1dbfe559 | ||
|
|
1fc49f2aa1 | ||
|
|
8d2d19d17b | ||
|
|
cc283f64a3 | ||
|
|
5ddc63c673 | ||
|
|
41d7bf87c0 | ||
|
|
771f7e9261 | ||
|
|
eb98a7e8be | ||
|
|
d4817e9ff2 | ||
|
|
e71973b16c | ||
|
|
d36608b4fd | ||
|
|
73eb83c9dc | ||
|
|
4928261366 | ||
|
|
c44fa60721 | ||
|
|
7afea3df90 | ||
|
|
e8018b7959 | ||
|
|
0b36a83cc4 | ||
|
|
dc66e92c48 | ||
|
|
17c60f897b | ||
|
|
c8de38ef23 | ||
|
|
c2761ef16c | ||
|
|
3b6e20908e | ||
|
|
e6479e9a53 | ||
|
|
01e6fbddf4 | ||
|
|
0f0afd62bb | ||
|
|
eee6fd9f54 | ||
|
|
a0fe43cd6e | ||
|
|
91b5e359bd | ||
|
|
13fc878168 | ||
|
|
641d43c6f3 | ||
|
|
bb471d2d6d | ||
|
|
cfb8678c59 | ||
|
|
437c4a78c1 | ||
|
|
df76c679ff | ||
|
|
d30c1b1385 | ||
|
|
0b113b9bab | ||
|
|
f3d7852ec4 | ||
|
|
824d315a3b | ||
|
|
72caab1b2b | ||
|
|
f430d1aab2 | ||
|
|
d21e7b8f1a | ||
|
|
750b819086 | ||
|
|
43a35c8447 | ||
|
|
022af2053a | ||
|
|
90213554bb | ||
|
|
6d87ce73b6 | ||
|
|
8c4ac632ca | ||
|
|
e819eec41f | ||
|
|
ac34684850 | ||
|
|
cf1cea751b | ||
|
|
df961860f3 | ||
|
|
55134d76c1 | ||
|
|
6739bf72c4 | ||
|
|
9f35dcd900 | ||
|
|
93ad148b03 | ||
|
|
ecaff28231 | ||
|
|
4abf44617c | ||
|
|
39250abf7d | ||
|
|
a711cbd475 | ||
|
|
975d31178b | ||
|
|
415c2821c8 | ||
|
|
f891f25f9d | ||
|
|
0042fadcb0 | ||
|
|
34fff4ef26 | ||
|
|
eea99b034e | ||
|
|
30664ea806 | ||
|
|
37d004d78d | ||
|
|
22062931fe | ||
|
|
d44f0d23ea | ||
|
|
2d0fdf144b | ||
|
|
727347331f | ||
|
|
8c25ccf1ea | ||
|
|
d59c563023 | ||
|
|
d5e35335bb | ||
|
|
43c48bf833 | ||
|
|
ea18a366c4 | ||
|
|
0a1eb877e0 | ||
|
|
16d6fd90a9 | ||
|
|
fac49c1da3 | ||
|
|
fb7bd4f14f | ||
|
|
74c0e409e7 | ||
|
|
1137c8d770 | ||
|
|
68dec70b34 | ||
|
|
dd52760095 | ||
|
|
770e3379fb | ||
|
|
7adfe65f65 | ||
|
|
57057911ab | ||
|
|
7a1fa71b76 | ||
|
|
d3af103812 | ||
|
|
0431b9561e | ||
|
|
f4e334c14e | ||
|
|
ef2f954b81 | ||
|
|
12041ac290 | ||
|
|
e6f5d535e9 | ||
|
|
f338941cfc | ||
|
|
3576f529ec | ||
|
|
5a5e896eb4 | ||
|
|
b2c1dbf3ab | ||
|
|
990a65a681 | ||
|
|
086640519a | ||
|
|
d99519cb23 | ||
|
|
aae0ef200d | ||
|
|
dfdffb0626 | ||
|
|
ae1e12f905 | ||
|
|
c49867a08d | ||
|
|
669e46266a | ||
|
|
755fa9e865 | ||
|
|
ed72713ba7 | ||
|
|
2fbcf17c8d | ||
|
|
f37fb47058 | ||
|
|
55758a2772 | ||
|
|
363be4ca34 | ||
|
|
5128481b23 | ||
|
|
fd3acd85e6 | ||
|
|
5b6a6cc9dc | ||
|
|
b50fdd637b | ||
|
|
fba1a5acb9 | ||
|
|
8babe7205b | ||
|
|
e02151b273 | ||
|
|
0c44ee5f88 | ||
|
|
ea9494019e | ||
|
|
e986869e96 | ||
|
|
50d6c5e61e | ||
|
|
da80793204 | ||
|
|
2d78e0ebb6 | ||
|
|
ac662ab3c1 | ||
|
|
456169b8d2 | ||
|
|
f3308827d0 | ||
|
|
d6cac3add8 | ||
|
|
42b4a03eb4 | ||
|
|
1e4bdcc324 | ||
|
|
c45cff87fc | ||
|
|
7de9ef3762 | ||
|
|
9ccdb4871d | ||
|
|
7173c5c737 | ||
|
|
64e9e48217 | ||
|
|
3de65daf85 | ||
|
|
7584d95149 | ||
|
|
d7fd48ef25 | ||
|
|
3969a396d3 | ||
|
|
c7dfd9dcf1 | ||
|
|
b0afbce97c | ||
|
|
5d478a5259 | ||
|
|
63780884c2 | ||
|
|
f05207580c | ||
|
|
27551e2975 | ||
|
|
76752a5dbd | ||
|
|
deeb2979f1 | ||
|
|
d19321733b | ||
|
|
f4d069c0cf | ||
|
|
241bf85f15 | ||
|
|
a5bac30ef3 | ||
|
|
b670bffa1b | ||
|
|
4c5490df94 | ||
|
|
d5ddebb7ac | ||
|
|
dc22c8df1f | ||
|
|
66ad75b759 | ||
|
|
b0f4a02dc6 | ||
|
|
4a7f497023 | ||
|
|
ecc68af439 | ||
|
|
79a05876bf | ||
|
|
3ea6d78d39 | ||
|
|
b766f0693a | ||
|
|
a19f0202b0 | ||
|
|
c6baff9bce | ||
|
|
63197d38ce | ||
|
|
e954b01921 | ||
|
|
1c8f94f1d8 | ||
|
|
3d17498945 | ||
|
|
869ce2b366 | ||
|
|
68bfd8bc25 | ||
|
|
fdd1167f33 | ||
|
|
4ce4790ed2 | ||
|
|
01a03e9baf | ||
|
|
df101258c5 | ||
|
|
13701498ce | ||
|
|
09f8dace9d | ||
|
|
88cbeb930d | ||
|
|
4d3a1efca3 | ||
|
|
27f7dd26ab | ||
|
|
ba41939c27 | ||
|
|
d5869aebc6 | ||
|
|
dfa3df8ea5 | ||
|
|
92f7b25117 | ||
|
|
6cb504cb08 | ||
|
|
e9de53ef98 | ||
|
|
d89c267e62 | ||
|
|
b59546b1ab | ||
|
|
b73950c1b4 | ||
|
|
3287ce6f90 | ||
|
|
9cf353b423 | ||
|
|
377d788223 | ||
|
|
7255cb49cb | ||
|
|
86b8dd4856 | ||
|
|
16c03444ab | ||
|
|
ccc77bfcfe | ||
|
|
b3caa87b78 | ||
|
|
8021381de2 | ||
|
|
65287ec4f3 | ||
|
|
bdc1adb2ed | ||
|
|
f2fffe5304 | ||
|
|
1c91c9939f | ||
|
|
ba83c01b6c | ||
|
|
d76423a305 | ||
|
|
dec0e3eec3 | ||
|
|
1fab973c67 | ||
|
|
70d7b3b70a | ||
|
|
c8095faa1f | ||
|
|
d5e6cc542f | ||
|
|
3c1113260d | ||
|
|
06b56db67c | ||
|
|
b3553e93ab | ||
|
|
7f7226e466 | ||
|
|
c060f9fd4d | ||
|
|
e70be7cf9e | ||
|
|
822035056a | ||
|
|
cf6e993a4d | ||
|
|
6f115d2db3 | ||
|
|
0845a4bf4c | ||
|
|
e0f591ebe3 | ||
|
|
68ea6fbb4f | ||
|
|
7ad2e7fbdb | ||
|
|
fd3c703e2e | ||
|
|
8d021ba7e1 | ||
|
|
c77fd1a6fc | ||
|
|
4025005251 | ||
|
|
3a8920a1e9 | ||
|
|
c94606dccd | ||
|
|
0957bdda20 | ||
|
|
f75b404387 | ||
|
|
959b8ed83e | ||
|
|
5b07046396 | ||
|
|
8e256462bf | ||
|
|
80673b572a | ||
|
|
f3677a49bf | ||
|
|
c22c7eff60 | ||
|
|
51af4011a3 | ||
|
|
02c35f963e | ||
|
|
4385acef99 | ||
|
|
bf0c716aa5 | ||
|
|
503f7286b9 | ||
|
|
0f9c6038ca | ||
|
|
c6cae162be | ||
|
|
5c7d20df54 | ||
|
|
2e5c7e0073 | ||
|
|
151bf5b49f | ||
|
|
1b7f52e013 | ||
|
|
00921ed839 | ||
|
|
c300af8241 | ||
|
|
b1b6a91db8 | ||
|
|
dbb24108f1 | ||
|
|
60b9496162 | ||
|
|
2eff7b95dd | ||
|
|
89fec7841c | ||
|
|
f83ccb6ca4 | ||
|
|
47f45a6275 | ||
|
|
a39991a9ca | ||
|
|
daee348795 | ||
|
|
fa2c4725be | ||
|
|
f02fa629cc | ||
|
|
d4fa9b7345 | ||
|
|
8d27111f7b | ||
|
|
c6ae0bb509 | ||
|
|
997ff74fb9 | ||
|
|
8476e36122 | ||
|
|
5e7f0f9d78 | ||
|
|
10fc0b071f | ||
|
|
38b6bb3952 | ||
|
|
921771d41b | ||
|
|
c53aed4cbf | ||
|
|
415006badc | ||
|
|
089c9657f9 | ||
|
|
0ca72bf444 | ||
|
|
6057b27dc8 | ||
|
|
a5d695ec92 | ||
|
|
f7163451e1 | ||
|
|
d2d2020573 | ||
|
|
e937d74b11 | ||
|
|
d778085ba5 | ||
|
|
b0ae4e9a60 | ||
|
|
fcb31ac51b | ||
|
|
5d2ac0b86b | ||
|
|
d4422e8901 | ||
|
|
da50b49e01 | ||
|
|
edcc0ba7ce | ||
|
|
d2065bfa1b | ||
|
|
7b17c3e36c | ||
|
|
acac33c217 | ||
|
|
5d7804478b | ||
|
|
5e52a12287 | ||
|
|
a23e57d9f7 | ||
|
|
2f902d412c | ||
|
|
e9ed08c8be | ||
|
|
a931ac1022 | ||
|
|
24f93ec3ef | ||
|
|
a9f95109f6 | ||
|
|
803b44280f | ||
|
|
318e05ddba | ||
|
|
000a4ec822 | ||
|
|
ed46ff3445 | ||
|
|
7a95fb55bd | ||
|
|
eaf7999d70 | ||
|
|
2633c82a01 | ||
|
|
12245d0956 | ||
|
|
6d87b5dfec | ||
|
|
bbd065940e | ||
|
|
61450a5ca4 | ||
|
|
05f0156661 | ||
|
|
14c943bd48 | ||
|
|
a03f6605ce | ||
|
|
a7e066fc11 | ||
|
|
7cd270d534 | ||
|
|
b14157797e | ||
|
|
0cff6c1fdf | ||
|
|
ca61ca5c40 | ||
|
|
aa7ebe4168 | ||
|
|
c34dc0be13 | ||
|
|
d5c2308587 | ||
|
|
7f0581018b | ||
|
|
6235225f7d | ||
|
|
ea9f622db2 | ||
|
|
64d949fc96 | ||
|
|
386315ad27 | ||
|
|
2b518ded37 | ||
|
|
ecd4cc7b72 | ||
|
|
86a53141ad | ||
|
|
0aa8ac5d39 | ||
|
|
29011cac5e | ||
|
|
9f36665d6d | ||
|
|
abe7a85a39 | ||
|
|
95e5e3132b | ||
|
|
99bf3cf3ba | ||
|
|
fedf2326f0 | ||
|
|
e8b658646a | ||
|
|
1875391a3a | ||
|
|
15b63778e5 | ||
|
|
a06b044342 | ||
|
|
c5d7cf4eeb | ||
|
|
8bbec88b6f | ||
|
|
fc5ab2fd59 | ||
|
|
003ef8747c | ||
|
|
4e07e3bd68 | ||
|
|
e0b6bde525 | ||
|
|
ee91cb99ce | ||
|
|
7440ca9ca8 | ||
|
|
238f99c8ca | ||
|
|
604f45bea4 | ||
|
|
d1a066f601 | ||
|
|
99509e61d3 | ||
|
|
1fe3c81b8d | ||
|
|
5d7166662c | ||
|
|
0d2660ee6c | ||
|
|
f489b6b506 | ||
|
|
c80a492d76 | ||
|
|
bbadf3c7e6 | ||
|
|
5c3fda48f7 | ||
|
|
2329ec25b9 | ||
|
|
c08ae534c5 | ||
|
|
816905a506 | ||
|
|
3598d6623a | ||
|
|
ae6a95bad8 | ||
|
|
634697af2d | ||
|
|
d320f8a713 | ||
|
|
b1e3638b34 | ||
|
|
947b9e75db | ||
|
|
8ad995e56f | ||
|
|
96fe74760f | ||
|
|
83eeb747dc | ||
|
|
bba5b425b3 | ||
|
|
f6eb6666c2 | ||
|
|
ee7e507cde | ||
|
|
162980ccaf | ||
|
|
8e54809c79 | ||
|
|
abc6e28401 | ||
|
|
567824ebec | ||
|
|
e827a965ad | ||
|
|
215d5069a9 | ||
|
|
fb4aa58a75 | ||
|
|
dd00c9b53e | ||
|
|
3c380954ec | ||
|
|
136432d098 | ||
|
|
1920bd53b6 | ||
|
|
ac8a7a9254 | ||
|
|
0039c1c393 | ||
|
|
02efc655f9 | ||
|
|
f43210d3d0 | ||
|
|
67dffcdc69 | ||
|
|
36ab3ecf71 | ||
|
|
e1b0dd00bb | ||
|
|
37a39d1624 | ||
|
|
9fe978319e | ||
|
|
2a15113a74 | ||
|
|
b5b43b8b3f | ||
|
|
057829c3b0 | ||
|
|
d0f13e16d5 | ||
|
|
abde842bf0 | ||
|
|
cf33e40e70 | ||
|
|
1b34780b7e | ||
|
|
53ffc9867c | ||
|
|
8b9d64b15a | ||
|
|
350dfeabba | ||
|
|
b0cbd6c6bf | ||
|
|
70c1722ef2 | ||
|
|
225430162b | ||
|
|
bc0cc2bfa9 | ||
|
|
c85d3119f9 | ||
|
|
5c07b39b04 | ||
|
|
2b39865251 | ||
|
|
ca9d9f093c | ||
|
|
0b7c00bfe6 | ||
|
|
6ef388e258 | ||
|
|
fb5b9c445b | ||
|
|
fdbed91a4e | ||
|
|
0b0d0a7353 | ||
|
|
304382f406 | ||
|
|
97de1b4622 | ||
|
|
24f1b5c0a7 | ||
|
|
532190ef4b | ||
|
|
779809b78b | ||
|
|
9f07b65877 | ||
|
|
1514613f61 | ||
|
|
5eb7fe958f | ||
|
|
24fa77e7b2 | ||
|
|
9ae411802d | ||
|
|
1a3e0a3163 | ||
|
|
6e601ede37 | ||
|
|
9bbd314452 | ||
|
|
b40c63dcfe | ||
|
|
21ed073f29 | ||
|
|
5f396a53c3 | ||
|
|
a368cbd265 | ||
|
|
a2cad761b9 | ||
|
|
2af273a4a1 | ||
|
|
01f2f754ea | ||
|
|
1b6e6f80b6 | ||
|
|
de7d6c68b8 | ||
|
|
31c73fe448 | ||
|
|
526e091d22 | ||
|
|
40736336db | ||
|
|
f2200f793d | ||
|
|
7fd391259c | ||
|
|
b225435164 | ||
|
|
be3152e630 | ||
|
|
bd374825f1 | ||
|
|
f395117885 | ||
|
|
ae9c2dd830 | ||
|
|
923db77751 | ||
|
|
78cac9fcfa | ||
|
|
5fe8884471 | ||
|
|
fb8143bb49 | ||
|
|
90e58e5a22 | ||
|
|
254561aeb1 | ||
|
|
9a25878104 | ||
|
|
54cfabec5c | ||
|
|
7dc629a647 | ||
|
|
cbb2802383 | ||
|
|
fd2399d589 | ||
|
|
33d012cfc0 | ||
|
|
066bf1220f | ||
|
|
fd87be6d1e | ||
|
|
fe591816bb | ||
|
|
98d987869c | ||
|
|
e10e5802f4 | ||
|
|
fd5113744f | ||
|
|
a22c946276 | ||
|
|
a862337ead | ||
|
|
79cf3079c3 | ||
|
|
6c4e3b1fd5 | ||
|
|
1368c7c6e3 | ||
|
|
cb158028df | ||
|
|
8f72bd5e69 | ||
|
|
793b723942 | ||
|
|
7f221c7834 | ||
|
|
944f420270 | ||
|
|
b89c7b8675 | ||
|
|
a3525252b7 | ||
|
|
b42004b32c | ||
|
|
b2268c7452 | ||
|
|
86dde88fe6 | ||
|
|
ab7083d263 | ||
|
|
9ad17a04ff | ||
|
|
23670bca12 | ||
|
|
bfc467dd96 | ||
|
|
ada9b944dc | ||
|
|
f6b4a463b1 | ||
|
|
d53320779c | ||
|
|
71c398f0fc | ||
|
|
64ec751938 | ||
|
|
08b642575f | ||
|
|
fee8244a74 | ||
|
|
328477a1c6 | ||
|
|
a771871cf8 | ||
|
|
1cde85bba8 | ||
|
|
6288936fe2 | ||
|
|
582402d45e | ||
|
|
5657a4df9c | ||
|
|
865b587bdd | ||
|
|
b179be78db | ||
|
|
5f7c7ee809 | ||
|
|
fb604483ce | ||
|
|
6510e2c898 | ||
|
|
e3040ad2e1 | ||
|
|
032b1e7a03 | ||
|
|
7189719e6f | ||
|
|
6a68e5ca92 | ||
|
|
a3e312b368 | ||
|
|
895abe8c3b | ||
|
|
ae47ee817f | ||
|
|
6c84518b40 | ||
|
|
3ba72fd33b | ||
|
|
c4e01c2020 | ||
|
|
80715f5e58 | ||
|
|
f868e8b964 | ||
|
|
4af18f7d4a | ||
|
|
2758adcc8c | ||
|
|
b6c1c81152 | ||
|
|
d263ff9a6b | ||
|
|
6705b59b23 | ||
|
|
fbc2566f70 | ||
|
|
ed24cd5b52 | ||
|
|
a6eb89e24b | ||
|
|
242d530bb4 | ||
|
|
3bf5476922 | ||
|
|
7698ae00dd | ||
|
|
a20222fbef | ||
|
|
b8f7ca0749 | ||
|
|
612d948eba | ||
|
|
a3f72bd4a0 | ||
|
|
2d15b8b78a | ||
|
|
12fba024f0 | ||
|
|
06b1c03053 | ||
|
|
ed0e69de53 | ||
|
|
d86c811543 | ||
|
|
ac59b7060e | ||
|
|
b1d69e3949 | ||
|
|
91ab518dfb | ||
|
|
d178dce0d3 | ||
|
|
286f73f38d | ||
|
|
7760248e6b | ||
|
|
4ddadd9a0d | ||
|
|
722a996ad3 | ||
|
|
9350f6a04c | ||
|
|
c76364a891 | ||
|
|
8a9e2dc90d | ||
|
|
c48838e5b6 | ||
|
|
16ff1176f7 | ||
|
|
6275737ced | ||
|
|
b24a40797f | ||
|
|
5f2af81dda | ||
|
|
b35d0b3f6a | ||
|
|
9ba2c8e53d | ||
|
|
4d3bfe3cf2 | ||
|
|
ac0d1c92c3 | ||
|
|
8430cb40ab | ||
|
|
65efa15551 | ||
|
|
e9eebd3ce6 | ||
|
|
aef89160e2 | ||
|
|
ea5ad24944 |
@@ -1,14 +0,0 @@
|
||||
FROM mono:5.18
|
||||
|
||||
RUN dpkg --add-architecture i386 && apt-get update && apt-get install -y git ssh tar gzip ca-certificates wget zip wine wine32 wine64 libwine libwine:i386
|
||||
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash -E -
|
||||
RUN apt-get install -y nodejs
|
||||
RUN wget https://mediaarea.net/repo/deb/repo-mediaarea_1.0-7_all.deb && dpkg -i repo-mediaarea_1.0-7_all.deb && apt-get update
|
||||
RUN apt-get install -y libmediainfo-dev libmediainfo0v5 mediainfo
|
||||
RUN npm i -g npm
|
||||
RUN apt-get install -y python3-pip && pip3 install gitchangelog pystache
|
||||
RUN curl -O https://dl.google.com/go/go1.10.2.linux-amd64.tar.gz && tar xvf go*.tar.gz && chown -R root:root ./go && mv go /usr/local
|
||||
ENV GOPATH=$HOME/work
|
||||
ENV PATH="${PATH}:/usr/local/go/bin:$GOPATH/bin"
|
||||
RUN go get github.com/aktau/github-release
|
||||
RUN npm install -g yarn
|
||||
@@ -1,169 +0,0 @@
|
||||
version: 2
|
||||
|
||||
defaults: &defaults
|
||||
docker:
|
||||
- image: gallileo/radarr-cci-primary:5.8.9
|
||||
environment:
|
||||
BUILD_VERSION: 0.2.0
|
||||
|
||||
jobs:
|
||||
build:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- source-v1-{{ .Branch }}-{{ .Revision }}
|
||||
- source-v1-{{ .Branch }}-
|
||||
- source-v1-
|
||||
- checkout
|
||||
- run: git submodule update --init --recursive
|
||||
- save_cache:
|
||||
key: source-v1-{{ .Branch }}-{{ .Revision }}
|
||||
paths:
|
||||
- ".git"
|
||||
- run:
|
||||
name: Patching Assembly Info
|
||||
command: sed -i "s/AssemblyVersion(\".*\")/AssemblyVersion(\"$BUILD_VERSION.$CIRCLE_BUILD_NUM\")/gi" src/NzbDrone.Common/Properties/SharedAssemblyInfo.cs && cat src/NzbDrone.Common/Properties/SharedAssemblyInfo.cs
|
||||
- run:
|
||||
name: Clean Build
|
||||
command: ./build.sh Clean
|
||||
- run:
|
||||
name: Restore Nuget
|
||||
command: ./build.sh NugetMono
|
||||
- run:
|
||||
name: Build
|
||||
command: ./build.sh Build
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-npm-deps-{{ checksum "package.json" }}
|
||||
# Find the most recent cache used from any branch
|
||||
- v1-npm-deps-
|
||||
- run:
|
||||
name: Gulp
|
||||
command: ./build.sh Gulp
|
||||
- save_cache:
|
||||
key: v1-npm-deps-{{ checksum "package.json" }}
|
||||
paths:
|
||||
- "node_modules"
|
||||
- run:
|
||||
name: Package
|
||||
command: ./build.sh Package
|
||||
- run:
|
||||
name: Preparing Tests
|
||||
command: mkdir -p _tests/reports/junit && mkdir -p ../.config/Radarr && chmod -R 777 ../.config
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
# Must be relative path from root
|
||||
paths:
|
||||
- _output
|
||||
- _output_mono
|
||||
- _output_osx
|
||||
- _output_osx_app
|
||||
- _tests
|
||||
- setup
|
||||
- .circleci
|
||||
- deploy.sh
|
||||
unit_tests:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Preparing Tests
|
||||
command: mkdir -p ../.config/Radarr && chmod -R 777 ../.config
|
||||
- run:
|
||||
name: Unit Tests
|
||||
command: ./_tests/test.sh Linux Unit
|
||||
- store_test_results:
|
||||
path: _tests/reports/
|
||||
|
||||
integration_tests:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Copy Binaries for Integration Tests
|
||||
command: cp -R _output_mono/ _tests/bin
|
||||
- run:
|
||||
name: Preparing Tests
|
||||
command: mkdir -p ../.config/Radarr && chmod -R 777 ../.config
|
||||
- run:
|
||||
name: Integration Tests
|
||||
command: ./_tests/test.sh Linux Integration
|
||||
- store_test_results:
|
||||
path: _tests/reports/
|
||||
publish_artifacts:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: "Creating packages"
|
||||
command: |
|
||||
mkdir -p _packages/
|
||||
cp -r _output/ _packages/Radarr
|
||||
zip -r _packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.windows.zip _packages/Radarr
|
||||
rm -rf _packages/Radarr
|
||||
cp -r _output_mono/ _packages/Radarr
|
||||
tar -zcvf _packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.linux.tar.gz -C _packages Radarr
|
||||
rm -rf _packages/Radarr
|
||||
cp -r _output_osx/ _packages/Radarr
|
||||
tar -zcvf _packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.osx.tar.gz -C _packages Radarr
|
||||
rm -rf _packages/Radarr
|
||||
cd _output_osx_app/
|
||||
zip -r ../_packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.osx-app.zip *
|
||||
- run:
|
||||
name: "Creating Installer"
|
||||
command: wine setup/inno/ISCC.exe setup/nzbdrone.iss && cp -r setup/Output/Radarr* _packages/
|
||||
- store_artifacts:
|
||||
path: _packages
|
||||
destination: artifacts
|
||||
#- run:
|
||||
# name: "Deploying"
|
||||
# command: chmod +x deploy.sh && ./deploy.sh
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
# Must be relative path from root
|
||||
paths:
|
||||
- _packages
|
||||
deploy:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- restore_cache:
|
||||
keys:
|
||||
- source-v1-{{ .Branch }}-{{ .Revision }}
|
||||
- source-v1-{{ .Branch }}-
|
||||
- source-v1-
|
||||
- checkout
|
||||
- run:
|
||||
name: Creating Release
|
||||
command: export LC_ALL=C.UTF-8 && export changelog=$(GITCHANGELOG_CONFIG_FILENAME=.gitchangelog.rc.release gitchangelog) && echo "Deploying v$BUILD_VERSION.$CIRCLE_BUILD_NUM to Github, with changelog:\n\n$changelog" && github-release release -u Radarr -r Radarr -t "v$BUILD_VERSION" -p --draft -d "$changelog" -n "Pre-Release v$BUILD_VERSION"
|
||||
- run:
|
||||
name: Uploading Assets
|
||||
command: cd _packages && ls Radarr.*.* | xargs -n1 -P0 -I{} -- github-release upload -u Radarr -r Radarr -t "v$BUILD_VERSION.$CIRCLE_BUILD_NUM" --name {} --file {}
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
|
||||
build_and_test:
|
||||
jobs:
|
||||
- build
|
||||
- unit_tests:
|
||||
requires:
|
||||
- build
|
||||
#- integration_tests:
|
||||
# requires:
|
||||
# - build
|
||||
- publish_artifacts:
|
||||
requires:
|
||||
- build
|
||||
#- request_deploy:
|
||||
# type: approval
|
||||
# requires:
|
||||
# - publish_artifacts
|
||||
#- deploy:
|
||||
# requires:
|
||||
# - request_deploy
|
||||
@@ -1,69 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||
<xsl:output method="xml" indent="yes"/>
|
||||
|
||||
<xsl:template match="/test-run">
|
||||
<testsuites tests="{@testcasecount}" failures="{@failed}" disabled="{@skipped}" time="{@duration}">
|
||||
<xsl:apply-templates/>
|
||||
</testsuites>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="test-suite">
|
||||
<xsl:if test="test-case">
|
||||
<testsuite tests="{@testcasecount}" time="{@duration}" errors="{@testcasecount - @passed - @skipped - @failed}" failures="{@failed}" skipped="{@skipped}" timestamp="{@start-time}">
|
||||
<xsl:attribute name="name">
|
||||
<xsl:for-each select="ancestor-or-self::test-suite/@name">
|
||||
<xsl:value-of select="concat(., '.')"/>
|
||||
</xsl:for-each>
|
||||
</xsl:attribute>
|
||||
<xsl:apply-templates select="test-case"/>
|
||||
</testsuite>
|
||||
<xsl:apply-templates select="test-suite"/>
|
||||
</xsl:if>
|
||||
<xsl:if test="not(test-case)">
|
||||
<xsl:apply-templates/>
|
||||
</xsl:if>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="test-case">
|
||||
<testcase name="{@name}" assertions="{@asserts}" time="{@duration}" status="{@result}" classname="{@classname}">
|
||||
<xsl:if test="@runstate = 'Skipped' or @runstate = 'Ignored'">
|
||||
<skipped/>
|
||||
</xsl:if>
|
||||
|
||||
<xsl:apply-templates/>
|
||||
</testcase>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="command-line"/>
|
||||
<xsl:template match="settings"/>
|
||||
|
||||
<xsl:template match="output">
|
||||
<system-out>
|
||||
<xsl:value-of select="."/>
|
||||
</system-out>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="stack-trace">
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="test-case/failure">
|
||||
<failure message="{./message}">
|
||||
<xsl:value-of select="./stack-trace"/>
|
||||
</failure>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="test-suite/failure"/>
|
||||
|
||||
<xsl:template match="test-case/reason">
|
||||
<skipped message="{./message}"/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="test-case/assertions">
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="test-suite/reason"/>
|
||||
|
||||
<xsl:template match="properties"/>
|
||||
</xsl:stylesheet>
|
||||
|
||||
246
.editorconfig
@@ -2,14 +2,256 @@
|
||||
# editorconfig.org
|
||||
root = true
|
||||
|
||||
[*.{cs,html,js,hbs}]
|
||||
# NOTE: Requires **VS2019 16.3** or later
|
||||
|
||||
# Stylecop.ruleset
|
||||
# Description: Rules for Radarr
|
||||
|
||||
# Code files
|
||||
[*.cs]
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.less]
|
||||
# Sort using and Import directives with System.* appearing first
|
||||
dotnet_sort_system_directives_first = true
|
||||
|
||||
# Avoid "this." and "Me." if not necessary
|
||||
dotnet_style_qualification_for_field = false:refactoring
|
||||
dotnet_style_qualification_for_property = false:refactoring
|
||||
dotnet_style_qualification_for_method = false:refactoring
|
||||
dotnet_style_qualification_for_event = false:refactoring
|
||||
|
||||
# Indentation preferences
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = true
|
||||
csharp_indent_switch_labels = true
|
||||
csharp_indent_labels = flush_left
|
||||
|
||||
dotnet_style_qualification_for_field = false:suggestion
|
||||
dotnet_style_qualification_for_property = false:suggestion
|
||||
dotnet_style_qualification_for_method = false:suggestion
|
||||
dotnet_style_qualification_for_event = false:suggestion
|
||||
dotnet_naming_style.instance_field_style.capitalization = camel_case
|
||||
dotnet_naming_style.instance_field_style.required_prefix = _
|
||||
|
||||
# Prefer "var" everywhere
|
||||
csharp_style_var_for_built_in_types = true:suggestion
|
||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||
csharp_style_var_elsewhere = true:suggestion
|
||||
|
||||
# Stylecop Rules
|
||||
dotnet_diagnostic.SA0001.severity = none
|
||||
dotnet_diagnostic.SA1005.severity = none
|
||||
dotnet_diagnostic.SA1025.severity = none
|
||||
dotnet_diagnostic.SA1101.severity = none
|
||||
dotnet_diagnostic.SA1116.severity = none
|
||||
dotnet_diagnostic.SA1118.severity = none
|
||||
dotnet_diagnostic.SA1122.severity = none
|
||||
dotnet_diagnostic.SA1201.severity = suggestion
|
||||
dotnet_diagnostic.SA1202.severity = suggestion
|
||||
dotnet_diagnostic.SA1204.severity = suggestion
|
||||
dotnet_diagnostic.SA1300.severity = none
|
||||
dotnet_diagnostic.SA1303.severity = none
|
||||
dotnet_diagnostic.SA1304.severity = none
|
||||
dotnet_diagnostic.SA1306.severity = none
|
||||
dotnet_diagnostic.SA1309.severity = none
|
||||
dotnet_diagnostic.SA1310.severity = none
|
||||
dotnet_diagnostic.SA1401.severity = none
|
||||
dotnet_diagnostic.SA1402.severity = none
|
||||
dotnet_diagnostic.SA1404.severity = suggestion
|
||||
dotnet_diagnostic.SA1405.severity = suggestion
|
||||
dotnet_diagnostic.SA1406.severity = suggestion
|
||||
dotnet_diagnostic.SA1410.severity = suggestion
|
||||
dotnet_diagnostic.SA1411.severity = suggestion
|
||||
dotnet_diagnostic.SA1413.severity = none
|
||||
dotnet_diagnostic.SA1516.severity = none
|
||||
dotnet_diagnostic.SA1600.severity = none
|
||||
dotnet_diagnostic.SA1601.severity = none
|
||||
dotnet_diagnostic.SA1602.severity = none
|
||||
dotnet_diagnostic.SA1604.severity = none
|
||||
dotnet_diagnostic.SA1605.severity = none
|
||||
dotnet_diagnostic.SA1606.severity = none
|
||||
dotnet_diagnostic.SA1607.severity = none
|
||||
dotnet_diagnostic.SA1608.severity = none
|
||||
dotnet_diagnostic.SA1610.severity = none
|
||||
dotnet_diagnostic.SA1611.severity = none
|
||||
dotnet_diagnostic.SA1612.severity = none
|
||||
dotnet_diagnostic.SA1613.severity = none
|
||||
dotnet_diagnostic.SA1614.severity = none
|
||||
dotnet_diagnostic.SA1615.severity = none
|
||||
dotnet_diagnostic.SA1616.severity = none
|
||||
dotnet_diagnostic.SA1617.severity = none
|
||||
dotnet_diagnostic.SA1618.severity = none
|
||||
dotnet_diagnostic.SA1619.severity = none
|
||||
dotnet_diagnostic.SA1620.severity = none
|
||||
dotnet_diagnostic.SA1621.severity = none
|
||||
dotnet_diagnostic.SA1622.severity = none
|
||||
dotnet_diagnostic.SA1623.severity = none
|
||||
dotnet_diagnostic.SA1624.severity = none
|
||||
dotnet_diagnostic.SA1625.severity = none
|
||||
dotnet_diagnostic.SA1626.severity = none
|
||||
dotnet_diagnostic.SA1627.severity = none
|
||||
dotnet_diagnostic.SA1629.severity = none
|
||||
dotnet_diagnostic.SA1633.severity = none
|
||||
dotnet_diagnostic.SA1634.severity = none
|
||||
dotnet_diagnostic.SA1635.severity = none
|
||||
dotnet_diagnostic.SA1636.severity = none
|
||||
dotnet_diagnostic.SA1637.severity = none
|
||||
dotnet_diagnostic.SA1638.severity = none
|
||||
dotnet_diagnostic.SA1640.severity = none
|
||||
dotnet_diagnostic.SA1641.severity = none
|
||||
dotnet_diagnostic.SA1642.severity = none
|
||||
dotnet_diagnostic.SA1643.severity = none
|
||||
dotnet_diagnostic.SA1648.severity = none
|
||||
dotnet_diagnostic.SA1649.severity = none
|
||||
dotnet_diagnostic.SA1651.severity = none
|
||||
dotnet_diagnostic.SX1101.severity = warning
|
||||
dotnet_diagnostic.SX1309.severity = warning
|
||||
|
||||
# Microsoft Analyzers that fail and need to be sorted thru
|
||||
dotnet_diagnostic.ASP0000.severity = suggestion
|
||||
dotnet_diagnostic.CA1000.severity = suggestion
|
||||
dotnet_diagnostic.CA1001.severity = suggestion
|
||||
dotnet_diagnostic.CA1003.severity = suggestion
|
||||
dotnet_diagnostic.CA1008.severity = suggestion
|
||||
dotnet_diagnostic.CA1010.severity = suggestion
|
||||
dotnet_diagnostic.CA1012.severity = suggestion
|
||||
dotnet_diagnostic.CA1014.severity = suggestion
|
||||
dotnet_diagnostic.CA1016.severity = suggestion
|
||||
dotnet_diagnostic.CA1017.severity = suggestion
|
||||
dotnet_diagnostic.CA1018.severity = suggestion
|
||||
dotnet_diagnostic.CA1019.severity = suggestion
|
||||
dotnet_diagnostic.CA1021.severity = suggestion
|
||||
dotnet_diagnostic.CA1024.severity = suggestion
|
||||
dotnet_diagnostic.CA1027.severity = suggestion
|
||||
dotnet_diagnostic.CA1028.severity = suggestion
|
||||
dotnet_diagnostic.CA1030.severity = suggestion
|
||||
dotnet_diagnostic.CA1031.severity = suggestion
|
||||
dotnet_diagnostic.CA1032.severity = suggestion
|
||||
dotnet_diagnostic.CA1033.severity = suggestion
|
||||
dotnet_diagnostic.CA1034.severity = suggestion
|
||||
dotnet_diagnostic.CA1036.severity = suggestion
|
||||
dotnet_diagnostic.CA1040.severity = suggestion
|
||||
dotnet_diagnostic.CA1041.severity = suggestion
|
||||
dotnet_diagnostic.CA1043.severity = suggestion
|
||||
dotnet_diagnostic.CA1044.severity = suggestion
|
||||
dotnet_diagnostic.CA1050.severity = suggestion
|
||||
dotnet_diagnostic.CA1051.severity = suggestion
|
||||
dotnet_diagnostic.CA1052.severity = suggestion
|
||||
dotnet_diagnostic.CA1054.severity = suggestion
|
||||
dotnet_diagnostic.CA1055.severity = suggestion
|
||||
dotnet_diagnostic.CA1056.severity = suggestion
|
||||
dotnet_diagnostic.CA1058.severity = suggestion
|
||||
dotnet_diagnostic.CA1060.severity = suggestion
|
||||
dotnet_diagnostic.CA1061.severity = suggestion
|
||||
dotnet_diagnostic.CA1062.severity = suggestion
|
||||
dotnet_diagnostic.CA1063.severity = suggestion
|
||||
dotnet_diagnostic.CA1064.severity = suggestion
|
||||
dotnet_diagnostic.CA1065.severity = suggestion
|
||||
dotnet_diagnostic.CA1066.severity = suggestion
|
||||
dotnet_diagnostic.CA1067.severity = suggestion
|
||||
dotnet_diagnostic.CA1068.severity = suggestion
|
||||
dotnet_diagnostic.CA1069.severity = suggestion
|
||||
dotnet_diagnostic.CA1200.severity = suggestion
|
||||
dotnet_diagnostic.CA1303.severity = suggestion
|
||||
dotnet_diagnostic.CA1304.severity = suggestion
|
||||
dotnet_diagnostic.CA1305.severity = suggestion
|
||||
dotnet_diagnostic.CA1307.severity = suggestion
|
||||
dotnet_diagnostic.CA1308.severity = suggestion
|
||||
dotnet_diagnostic.CA1401.severity = suggestion
|
||||
dotnet_diagnostic.CA1507.severity = suggestion
|
||||
dotnet_diagnostic.CA1707.severity = suggestion
|
||||
dotnet_diagnostic.CA1710.severity = suggestion
|
||||
dotnet_diagnostic.CA1712.severity = suggestion
|
||||
dotnet_diagnostic.CA1714.severity = suggestion
|
||||
dotnet_diagnostic.CA1715.severity = suggestion
|
||||
dotnet_diagnostic.CA1716.severity = suggestion
|
||||
dotnet_diagnostic.CA1717.severity = suggestion
|
||||
dotnet_diagnostic.CA1720.severity = suggestion
|
||||
dotnet_diagnostic.CA1721.severity = suggestion
|
||||
dotnet_diagnostic.CA1724.severity = suggestion
|
||||
dotnet_diagnostic.CA1801.severity = suggestion
|
||||
dotnet_diagnostic.CA1802.severity = suggestion
|
||||
dotnet_diagnostic.CA1805.severity = suggestion
|
||||
dotnet_diagnostic.CA1806.severity = suggestion
|
||||
dotnet_diagnostic.CA1810.severity = suggestion
|
||||
dotnet_diagnostic.CA1812.severity = suggestion
|
||||
dotnet_diagnostic.CA1814.severity = suggestion
|
||||
dotnet_diagnostic.CA1815.severity = suggestion
|
||||
dotnet_diagnostic.CA1816.severity = suggestion
|
||||
dotnet_diagnostic.CA1819.severity = suggestion
|
||||
dotnet_diagnostic.CA1822.severity = suggestion
|
||||
dotnet_diagnostic.CA1823.severity = suggestion
|
||||
dotnet_diagnostic.CA1824.severity = suggestion
|
||||
dotnet_diagnostic.CA2000.severity = suggestion
|
||||
dotnet_diagnostic.CA2002.severity = suggestion
|
||||
dotnet_diagnostic.CA2007.severity = suggestion
|
||||
dotnet_diagnostic.CA2008.severity = suggestion
|
||||
dotnet_diagnostic.CA2009.severity = suggestion
|
||||
dotnet_diagnostic.CA2010.severity = suggestion
|
||||
dotnet_diagnostic.CA2011.severity = suggestion
|
||||
dotnet_diagnostic.CA2012.severity = suggestion
|
||||
dotnet_diagnostic.CA2013.severity = suggestion
|
||||
dotnet_diagnostic.CA2100.severity = suggestion
|
||||
dotnet_diagnostic.CA2101.severity = suggestion
|
||||
dotnet_diagnostic.CA2119.severity = suggestion
|
||||
dotnet_diagnostic.CA2153.severity = suggestion
|
||||
dotnet_diagnostic.CA2200.severity = suggestion
|
||||
dotnet_diagnostic.CA2207.severity = suggestion
|
||||
dotnet_diagnostic.CA2208.severity = suggestion
|
||||
dotnet_diagnostic.CA2211.severity = suggestion
|
||||
dotnet_diagnostic.CA2213.severity = suggestion
|
||||
dotnet_diagnostic.CA2214.severity = suggestion
|
||||
dotnet_diagnostic.CA2215.severity = suggestion
|
||||
dotnet_diagnostic.CA2216.severity = suggestion
|
||||
dotnet_diagnostic.CA2219.severity = suggestion
|
||||
dotnet_diagnostic.CA2225.severity = suggestion
|
||||
dotnet_diagnostic.CA2226.severity = suggestion
|
||||
dotnet_diagnostic.CA2227.severity = suggestion
|
||||
dotnet_diagnostic.CA2229.severity = suggestion
|
||||
dotnet_diagnostic.CA2231.severity = suggestion
|
||||
dotnet_diagnostic.CA2234.severity = suggestion
|
||||
dotnet_diagnostic.CA2235.severity = suggestion
|
||||
dotnet_diagnostic.CA2237.severity = suggestion
|
||||
dotnet_diagnostic.CA2241.severity = suggestion
|
||||
dotnet_diagnostic.CA2242.severity = suggestion
|
||||
dotnet_diagnostic.CA2243.severity = suggestion
|
||||
dotnet_diagnostic.CA2244.severity = suggestion
|
||||
dotnet_diagnostic.CA2245.severity = suggestion
|
||||
dotnet_diagnostic.CA2246.severity = suggestion
|
||||
dotnet_diagnostic.CA3061.severity = suggestion
|
||||
dotnet_diagnostic.CA3075.severity = suggestion
|
||||
dotnet_diagnostic.CA3076.severity = suggestion
|
||||
dotnet_diagnostic.CA3077.severity = suggestion
|
||||
dotnet_diagnostic.CA3147.severity = suggestion
|
||||
dotnet_diagnostic.CA5350.severity = suggestion
|
||||
dotnet_diagnostic.CA5351.severity = suggestion
|
||||
dotnet_diagnostic.CA5359.severity = suggestion
|
||||
dotnet_diagnostic.CA5360.severity = suggestion
|
||||
dotnet_diagnostic.CA5363.severity = suggestion
|
||||
dotnet_diagnostic.CA5364.severity = suggestion
|
||||
dotnet_diagnostic.CA5365.severity = suggestion
|
||||
dotnet_diagnostic.CA5366.severity = suggestion
|
||||
dotnet_diagnostic.CA5368.severity = suggestion
|
||||
dotnet_diagnostic.CA5369.severity = suggestion
|
||||
dotnet_diagnostic.CA5370.severity = suggestion
|
||||
dotnet_diagnostic.CA5371.severity = suggestion
|
||||
dotnet_diagnostic.CA5372.severity = suggestion
|
||||
dotnet_diagnostic.CA5373.severity = suggestion
|
||||
dotnet_diagnostic.CA5374.severity = suggestion
|
||||
dotnet_diagnostic.CA5379.severity = suggestion
|
||||
dotnet_diagnostic.CA5384.severity = suggestion
|
||||
dotnet_diagnostic.CA5385.severity = suggestion
|
||||
dotnet_diagnostic.CA5397.severity = suggestion
|
||||
|
||||
|
||||
|
||||
[*.{js,html,js,hbs,less,css}]
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
9
.esprintrc
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"paths": [
|
||||
"frontend/src/**/*.js"
|
||||
],
|
||||
"ignored": [
|
||||
"**/node_modules/**/*"
|
||||
],
|
||||
"port": 5004
|
||||
}
|
||||
24
.gitattributes
vendored
@@ -1,22 +1,10 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
*text eol=lf
|
||||
* text=auto
|
||||
|
||||
# Explicitly set bash scripts to have unix endings
|
||||
*.sh text eol=lf
|
||||
macOS/Radarr text eol=lf
|
||||
|
||||
# Custom for Visual Studio
|
||||
*.cs diff=csharp
|
||||
#*.sln merge=union
|
||||
#*.csproj merge=union
|
||||
#*.vbproj merge=union
|
||||
#*.fsproj merge=union
|
||||
#*.dbproj merge=union
|
||||
|
||||
# Standard to msysgit
|
||||
*.doc diff=astextplain
|
||||
*.DOC diff=astextplain
|
||||
*.docx diff=astextplain
|
||||
*.DOCX diff=astextplain
|
||||
*.dot diff=astextplain
|
||||
*.DOT diff=astextplain
|
||||
*.pdf diff=astextplain
|
||||
*.PDF diff=astextplain
|
||||
*.rtf diff=astextplain
|
||||
*.RTF diff=astextplain
|
||||
*.sln merge=union
|
||||
|
||||
2
.github/ISSUE_TEMPLATE.md
vendored
@@ -6,7 +6,7 @@
|
||||
|
||||
**Just because you receive an exception in your logs, doesn't mean it's a bug and should be reported here. Often it's something else, such as a permission error. If you are unsure ask on the Discord or Subreddit first.**
|
||||
|
||||
Visit our [Discord server](https://discord.gg/NWYch8M) or [Subreddit](https://reddit.com/r/radarr) for support or longer discussions. Support questions posed on here will be closed immediately.
|
||||
Visit our [Discord server](https://discord.gg/r5wJPt9) or [Subreddit](https://reddit.com/r/radarr) for support or longer discussions. Support questions posed on here will be closed immediately.
|
||||
|
||||
Provide a description of the feature request or bug here, the more details the better.
|
||||
Please also include the following if you are reporting a bug. If you do not include it, the issue will probably be closed as we cannot help you. -->
|
||||
|
||||
7
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -2,6 +2,9 @@
|
||||
name: Bug report
|
||||
about: Support requests will be closed immediately, if you are unsure go to our Discord
|
||||
or Subreddit first. Exceptions do not mean you found a bug!
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
@@ -21,11 +24,11 @@ A clear and concise description of what you expected to happen.
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
**Platform Information (please complete the following information):**
|
||||
- OS: [e.g. Windows]
|
||||
- Mono Version: [e.g. Mono 5.8] (Only needed under Linux and Mac, found under System -> Status)
|
||||
- Browser and Version [e.g. chrome, safari] (Only needed for UI issues)
|
||||
- Version [e.g. 22]
|
||||
- Radarr Version [e.g. 3.0.0.2956]
|
||||
|
||||
**Debug Logs**
|
||||
Turn on debug logs under Settings -> General and wait for the bug to occur again. **Upload the full log file here (or another site and link it). Issues will be closed, if they do not include this!**
|
||||
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Support via Discord
|
||||
url: https://discord.gg/r5wJPt9
|
||||
about: Chat with users and devs on support and setup related topics.
|
||||
- name: Support via Reddit
|
||||
url: https://reddit.com/r/radarr
|
||||
about: Discuss and search thru support topics.
|
||||
3
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: feature request
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -6,6 +6,7 @@ YES | NO
|
||||
|
||||
#### Todos
|
||||
- [ ] Tests
|
||||
- [ ] Translation Keys
|
||||
|
||||
#### Issues Fixed or Closed by this PR
|
||||
|
||||
|
||||
5
.github/stale.yml
vendored
@@ -7,6 +7,10 @@ exemptLabels:
|
||||
- feature request
|
||||
- parser
|
||||
- confirmed
|
||||
- sonarr-pull
|
||||
- lidarr-pull
|
||||
- readarr-pull
|
||||
- v3
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
@@ -14,3 +18,4 @@ markComment: >
|
||||
This issue has been automatically marked as stale because it has not had recent activity. Please verify that this is still an issue with the latest version of Radarr and report back. Otherwise this issue will be closed.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
only: issues
|
||||
|
||||
2
.github/support.yml
vendored
@@ -6,7 +6,7 @@ supportLabel: support
|
||||
# to a support page, or set to `false` to disable
|
||||
supportComment: >
|
||||
We use the issue tracker exclusively for bug reports and feature requests.
|
||||
However, this issue appears to be a support request. Please hop over onto our [Discord](https://discord.gg/ZDmT7qb) or [Subreddit](https://reddit.com/r/radarr)
|
||||
However, this issue appears to be a support request. Please hop over onto our [Discord](https://discord.gg/r5wJPt9) or [Subreddit](https://reddit.com/r/radarr)
|
||||
# Whether to close issues marked as support requests
|
||||
close: true
|
||||
# Whether to lock issues marked as support requests
|
||||
|
||||
32
.gitignore
vendored
@@ -11,6 +11,7 @@ src/**/[Oo]bj/
|
||||
*.user
|
||||
*.sln.docstates
|
||||
.vs/
|
||||
.vscode/
|
||||
|
||||
# Build results
|
||||
*_i.c
|
||||
@@ -45,6 +46,10 @@ _dotCover*
|
||||
# DevExpress CodeRush
|
||||
src/.cr/
|
||||
|
||||
# Emacs
|
||||
*~
|
||||
\#*\#
|
||||
|
||||
# NCrunch
|
||||
*.ncrunch*
|
||||
.*crunch*.local.xml
|
||||
@@ -80,7 +85,6 @@ TestResults
|
||||
[Tt]est[Rr]esult*
|
||||
*.Cache
|
||||
ClientBin
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*.dbmdl
|
||||
Generated_Code #added for RIA/Silverlight projects
|
||||
@@ -102,40 +106,44 @@ _NCrunch_*
|
||||
_TeamCity*
|
||||
|
||||
# Radarr
|
||||
Backups/
|
||||
logs/
|
||||
#MediaCover/
|
||||
UpdateLogs/
|
||||
xdg/
|
||||
config.xml
|
||||
logs.db*
|
||||
nzbdrone.db*
|
||||
nzbdrone.pid
|
||||
nzbdrone.log*txt
|
||||
UpdateLogs/
|
||||
*workspace.xml
|
||||
*.test-cache
|
||||
*.userprefs
|
||||
*/test-results/*
|
||||
src/UI/.idea/*
|
||||
*log.txt
|
||||
node_modules/
|
||||
_output*
|
||||
_artifacts
|
||||
_rawPackage/
|
||||
_dotTrace*
|
||||
_tests/
|
||||
*.Result.xml
|
||||
coverage*.xml
|
||||
coverage*.json
|
||||
setup/Output/
|
||||
*.~is
|
||||
|
||||
UI.Phantom/
|
||||
|
||||
# VS outout folders
|
||||
bin
|
||||
obj
|
||||
output/*
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
**/Properties/launchSettings.json
|
||||
|
||||
# Packages
|
||||
Radarr_*/
|
||||
Radarr_*.zip
|
||||
Radarr_*.gz
|
||||
gecko.zip
|
||||
geckodriver.exe
|
||||
|
||||
# macOS metadata files
|
||||
._*
|
||||
@@ -163,6 +171,8 @@ packages.config.md5sum
|
||||
**/.idea/**/tasks.xml
|
||||
**/.idea/shelf/*
|
||||
**/.idea/dictionaries
|
||||
**/.idea/.idea.Radarr.Posix
|
||||
**/.idea/.idea.Radarr.Windows
|
||||
|
||||
# Sensitive or high-churn files
|
||||
**/.idea/**/dataSources/
|
||||
|
||||
4
.gitmodules
vendored
@@ -1,4 +0,0 @@
|
||||
[submodule "src/ExternalModules/CurlSharp"]
|
||||
path = src/ExternalModules/CurlSharp
|
||||
url = https://github.com/Sonarr/CurlSharp.git
|
||||
branch = master
|
||||
16
.travis.yml
@@ -1,16 +0,0 @@
|
||||
language: csharp
|
||||
solution: src/NzbDrone.sln
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- nodejs
|
||||
# - npm apparently not needed anymore.
|
||||
script:
|
||||
- ./build.sh
|
||||
- chmod +x test.sh
|
||||
# - ./test.sh Linux Unit Takes far too long, maybe even crashes travis :/
|
||||
after_success:
|
||||
- chmod +x package.sh
|
||||
- ./package.sh
|
||||
notifications:
|
||||
- webhooks: https://discordapp.com/api/webhooks/266910310219251712/V-QvCcnYkg3O8PMevcAJOJyCgrYkZQoF2pupLDGbaISNUECmYPd6LRwl3avKHsPyfgWP
|
||||
@@ -7,28 +7,14 @@ Setup guides, FAQ, the more information we have on the wiki the better.
|
||||
|
||||
## Development ##
|
||||
|
||||
### Tools required ###
|
||||
- Visual Studio 2015
|
||||
- HTML/Javascript editor of choice (Sublime Text/Webstorm/Atom/etc)
|
||||
- npm (node package manager)
|
||||
- git
|
||||
|
||||
### Getting started ###
|
||||
|
||||
1. Fork Radarr
|
||||
2. Clone (develop branch) *you may need pull in submodules separately if you client doesn't clone them automatically (CurlSharp)*
|
||||
3. Run `npm install`
|
||||
4. Run `npm start` - Used to compile the UI components and copy them.
|
||||
Leave this window open.
|
||||
If you have gulp globally installed you can use `gulp watch` instead
|
||||
5. Compile in Visual Studio
|
||||
See the readme for information on setting up your development environment.
|
||||
|
||||
### Contributing Code ###
|
||||
- If you're adding a new, already requested feature, please comment on [Github Issues](https://github.com/Radarr/Radarr/issues "Github Issues") so work is not duplicated (If you want to add something not already on there, please talk to us first)
|
||||
- Rebase from Radarr's develop branch, don't merge
|
||||
- Make meaningful commits, or squash them
|
||||
- Feel free to make a pull request before work is complete, this will let us see where its at and make comments/suggest improvements
|
||||
- Reach out to us on the forums or on IRC if you have any questions
|
||||
- Reach out to us on the discord if you have any questions
|
||||
- Add tests (unit/integration)
|
||||
- Commit with *nix line endings for consistency (We checkout Windows and commit *nix)
|
||||
- One feature/bug fix per pull request to keep things clean and easy to understand
|
||||
|
||||
BIN
Logo/1024.png
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 49 KiB |
BIN
Logo/128.png
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 4.1 KiB |
BIN
Logo/16.png
|
Before Width: | Height: | Size: 605 B After Width: | Height: | Size: 577 B |
BIN
Logo/256.png
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 10 KiB |
BIN
Logo/32.png
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.1 KiB |
BIN
Logo/400.png
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
BIN
Logo/48.png
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
Logo/512.png
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
BIN
Logo/64.png
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 1.9 KiB |
BIN
Logo/800.png
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 1.3 KiB |
BIN
Logo/text256.png
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 32 KiB |
74
README.md
@@ -1,43 +1,46 @@
|
||||
<p align="center">
|
||||
<img src="/Logo/text256.png" alt="Radarr">
|
||||
</p>
|
||||
# Radarr
|
||||
|
||||
[](https://dev.azure.com/Radarr/Radarr/_build/latest?definitionId=1&branchName=develop)
|
||||
[](https://translate.servarr.com/engage/radarr/?utm_source=widget)
|
||||
[](https://github.com/Radarr/Radarr/wiki/Docker)
|
||||

|
||||
[](#backers) [](#sponsors)
|
||||
|
||||
Radarr is an __independent__ fork of [Sonarr](https://github.com/Sonarr/Sonarr) reworked for automatically downloading movies via Usenet and BitTorrent.
|
||||
|
||||
The project was inspired by other Usenet/BitTorrent movie downloaders such as CouchPotato.
|
||||
|
||||
See the [Roadmap blogpost](https://blog.radarr.video/development/update/2018/11/11/roadmap-update.html) for an overview of planned features.
|
||||
|
||||
## Getting Started
|
||||
|
||||
[](https://github.com/Radarr/Radarr/wiki/Installation)
|
||||
[](https://github.com/Radarr/Radarr/wiki/Docker)
|
||||
[](https://github.com/Radarr/Radarr/wiki/Docker)
|
||||
[](https://github.com/Radarr/Radarr/wiki/Setup-Guide)
|
||||
[](https://github.com/Radarr/Radarr/wiki/FAQ)
|
||||
|
||||
|
||||
If you are using Docker please ensure your Docker paths are setup correctly using [this guide to facilitate](https://old.reddit.com/r/usenet/wiki/docker) hardlinks and minimize permissions issues.
|
||||
|
||||
* [Install Radarr for your desired OS](https://github.com/Radarr/Radarr/wiki/Installation) *or* use [Docker](https://github.com/Radarr/Radarr/wiki/Docker)
|
||||
* *For Linux users*, run `radarr` and *optionally* have [Radarr start automatically](https://github.com/Radarr/Radarr/wiki/Autostart-on-Linux)
|
||||
* Connect to the UI through <http://localhost:7878> or <http://your-ip:7878> in your web browser
|
||||
* See the [Setup Guide](https://github.com/Radarr/Radarr/wiki/Setup-Guide) for further configuration
|
||||
|
||||
## Downloads
|
||||
Please note that v0.2 will only have critical bugs resolved as of August 2020. Any additional development or features will be soley in V3.
|
||||
|
||||
Branch | develop (stable) | nightly (semi-unstable) |
|
||||
---|---|---
|
||||
Binary Releases | [](https://github.com/Radarr/Radarr/releases) | [](https://ci.appveyor.com/project/galli-leo/radarr-usby1/branch/develop/artifacts)
|
||||
Docker (linuxserver.io, x86_64, arm64, armhf) | [](https://store.docker.com/community/images/linuxserver/radarr) | [](https://store.docker.com/community/images/linuxserver/radarr)
|
||||
Docker (hotio, see [here](https://github.com/hotio/docker-radarr) for more information) | [](https://hub.docker.com/r/hotio/radarr) | [](https://hub.docker.com/r/hotio/radarr)
|
||||
Each push to the "develop" branch creates a build on "nightly" release channel (release channel is the "branch" within radarr's settings), once we push a build to Github it will show up on "develop" release channel.
|
||||
|
||||
|
||||
| Release Channel Type | Branch: develop (stable) (v0.2) | Branch: nightly (semi-unstable) (v3.0) |
|
||||
|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Binary Releases | [](https://github.com/Radarr/Radarr/releases) | [](https://radarr.servarr.com/v1/update/nightly/updatefile?os=windows&runtime=netcore&arch=x64) <br> [](https://radarr.servarr.com/v1/update/nightly/updatefile?os=linux&runtime=netcore&arch=x64) <br> [](https://radarr.servarr.com/v1/update/nightly/updatefile?os=linux&runtime=netcore&arch=arm64) [](https://radarr.servarr.com/v1/update/nightly/updatefile?os=linux&runtime=netcore&arch=arm) <br> [](https://radarr.servarr.com/v1/update/nightly/updatefile?os=osx&runtime=netcore&arch=x64)
|
||||
| Docker - lsio | [](https://hub.docker.com/r/linuxserver/radarr) | [](https://hub.docker.com/r/linuxserver/radarr) |
|
||||
| Docker - hotio | [](https://hub.docker.com/r/hotio/radarr) | [](https://hub.docker.com/r/hotio/radarr) |
|
||||
|
||||
## Support
|
||||
|
||||
[](#backers)
|
||||
[](#flexible-sponsors)
|
||||
[](#sponsors)
|
||||
|
||||
[](https://discord.gg/AD3UP37)
|
||||
[](https://discord.gg/r5wJPt9)
|
||||
[](https://www.reddit.com/r/radarr)
|
||||
[](http://feathub.com/Radarr/Radarr)
|
||||
[](https://github.com/Radarr/Radarr/issues)
|
||||
[](https://github.com/Radarr/Radarr/wiki)
|
||||
|
||||
@@ -46,23 +49,13 @@ Docker (hotio, see [here](https://github.com/hotio/docker-radarr) for more infor
|
||||
[](https://github.com/Radarr/Radarr/issues)
|
||||
[](https://github.com/Radarr/Radarr/pulls)
|
||||
[](http://www.gnu.org/licenses/gpl.html)
|
||||
[](https://github.com/Radarr/Radarr)
|
||||
[](https://github.com/Radarr/Radarr)
|
||||
[](https://github.com/Radarr/Radarr/releases/)
|
||||
[](https://hub.docker.com/r/linuxserver/radarr/)
|
||||
[](/CHANGELOG.md#unreleased)
|
||||
|
||||
| Service | Master | Develop |
|
||||
|----------|:---------------------------:|:----------------------------:|
|
||||
| AppVeyor | [](https://ci.appveyor.com/project/galli-leo/Radarr) | [](https://ci.appveyor.com/project/galli-leo/Radarr-usby1) |
|
||||
| Travis | [](https://travis-ci.org/Radarr/Radarr) | [](https://travis-ci.org/Radarr/Radarr) |
|
||||
|
||||
### [Site and API Status](https://status.radarr.video)
|
||||
|
||||
| API | Updates | Sites |
|
||||
|-------|:----:|:----:|
|
||||
| [](https://api.radarr.video/v2/) | [](https://radarr.aeonlucid.com) | [](https://mappings.radarr.video/)
|
||||
| [](https://staging.api.radarr.video/) | [](https://api.github.com/v3/) | [](https://radarr.video/)
|
||||
|
||||
Radarr is currently undergoing rapid development and pull requests are actively added into the repository.
|
||||
|
||||
## Features
|
||||
@@ -85,34 +78,30 @@ Radarr is currently undergoing rapid development and pull requests are actively
|
||||
* New TorrentPotato Indexer
|
||||
* Torznab Indexer now supports Movies (Works well with [Jackett](https://github.com/Jackett/Jackett))
|
||||
* Scanning PreDB to know when a new release is available
|
||||
* Importing movies from various online sources, such as IMDb Watchlists (A complete list can be found [here](https://github.com/Radarr/Radarr/issues/114))
|
||||
* Importing movies from various online sources, such as IMDb Watchlists or Trakt (v3) (A complete list can be found [here](https://github.com/Radarr/Radarr/issues/114))
|
||||
* Full integration with Kodi, Plex (notification, library update)
|
||||
* And a beautiful UI
|
||||
* And a new beautiful UI (v3)
|
||||
* Importing Metadata such as trailers or subtitles
|
||||
* Adding metadata such as posters and information for Kodi and others to use
|
||||
* Advanced customization for profiles, such that Radarr will always download the copy you want
|
||||
|
||||
### Planned Features
|
||||
|
||||
See the [Roadmap blogpost](https://blog.radarr.video/development/update/2018/11/11/roadmap-update.html) for an overview of planned features.
|
||||
|
||||
#### [Feature Requests](http://feathub.com/Radarr/Radarr)
|
||||
#### [Feature Requests](https://github.com/Radarr/Radarr/issues/new?assignees=&labels=feature+request&template=feature_request.md&title=)
|
||||
|
||||
## Configuring the Development Environment
|
||||
|
||||
### Requirements
|
||||
|
||||
* [Visual Studio Community 2017](https://www.visualstudio.com/vs/community/) or [Rider](http://www.jetbrains.com/rider/)
|
||||
* [Visual Studio Community 2019](https://www.visualstudio.com/vs/community/) or [Rider](http://www.jetbrains.com/rider/)
|
||||
* [Git](https://git-scm.com/downloads)
|
||||
* [Node.js](https://nodejs.org/en/download/)
|
||||
* [Yarn](https://yarnpkg.com/)
|
||||
|
||||
### Setup
|
||||
|
||||
* Make sure all the required software mentioned above are installed
|
||||
* Clone the repository into your development machine ([*info*](https://help.github.com/desktop/guides/contributing/working-with-your-remote-repository-on-github-or-github-enterprise))
|
||||
* Grab the submodules `git submodule init && git submodule update`
|
||||
* Install the required Node Packages `npm install`
|
||||
* Start gulp to monitor your dev environment for any changes that need post processing using `npm start` command.
|
||||
* Install the required Node Packages `yarn install`
|
||||
* Start gulp to monitor your dev environment for any changes that need post processing using `yarn start` command.
|
||||
|
||||
> **Notice**
|
||||
> Gulp must be running at all times while you are working with Radarr client source files.
|
||||
@@ -125,9 +114,9 @@ See the [Roadmap blogpost](https://blog.radarr.video/development/update/2018/11/
|
||||
|
||||
### Development
|
||||
|
||||
* Open `NzbDrone.sln` in Visual Studio 2017 or run the build.sh script, if Mono is installed. Alternatively you can use Jetbrains Rider, since it works on all Platforms.
|
||||
* Open `Radarr.sln` in Visual Studio 2019 or run the build.sh script, if Mono is installed. Alternatively you can use Jetbrains Rider, since it works on all Platforms.
|
||||
* Make sure `NzbDrone.Console` is set as the startup project
|
||||
* Run `build.sh` before running
|
||||
* Run `build.sh` before running, or build in VS
|
||||
|
||||
## Supporters
|
||||
|
||||
@@ -148,6 +137,7 @@ This project would not be possible without the support by these amazing folks. [
|
||||
### JetBrains
|
||||
|
||||
Thank you to [<img src="/Logo/jetbrains.svg" alt="JetBrains" width="32"> JetBrains](http://www.jetbrains.com/) for providing us with free licenses to their great tools
|
||||
|
||||
* [<img src="/Logo/resharper.svg" alt="ReSharper" width="32"> ReSharper](http://www.jetbrains.com/resharper/)
|
||||
* [<img src="/Logo/webstorm.svg" alt="WebStorm" width="32"> WebStorm](http://www.jetbrains.com/webstorm/)
|
||||
* [<img src="/Logo/rider.svg" alt="Rider" width="32"> Rider](http://www.jetbrains.com/rider/)
|
||||
@@ -156,4 +146,4 @@ Thank you to [<img src="/Logo/jetbrains.svg" alt="JetBrains" width="32"> JetBrai
|
||||
## License
|
||||
|
||||
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
|
||||
* Copyright 2010-2018
|
||||
* Copyright 2010-2020
|
||||
|
||||
59
appveyor.yml
@@ -1,59 +0,0 @@
|
||||
version: '0.2.0.{build}'
|
||||
|
||||
image: Visual Studio 2017
|
||||
|
||||
assembly_info:
|
||||
patch: true
|
||||
file: 'src\NzbDrone.Common\Properties\SharedAssemblyInfo.cs'
|
||||
assembly_version: '{version}'
|
||||
assembly_file_version: '{version}'
|
||||
assembly_informational_version: '{version}-rc1'
|
||||
|
||||
environment:
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
|
||||
install:
|
||||
- git submodule update --init --recursive
|
||||
|
||||
#init:
|
||||
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
|
||||
build_script:
|
||||
- ps: ./build-appveyor.ps1
|
||||
|
||||
test: off
|
||||
#test:
|
||||
# assemblies:
|
||||
# - '_tests\*Test.dll'
|
||||
# categories:
|
||||
# except:
|
||||
# - IntegrationTest
|
||||
# - AutomationTest
|
||||
|
||||
artifacts:
|
||||
- path: '_artifacts\*.zip'
|
||||
- path: '_artifacts\*.exe'
|
||||
- path: '_artifacts\*.tar.gz'
|
||||
|
||||
cache:
|
||||
- '%USERPROFILE%\.nuget\packages'
|
||||
- node_modules -> package.json
|
||||
|
||||
pull_requests:
|
||||
do_not_increment_build_number: true
|
||||
|
||||
on_failure:
|
||||
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
- ps: Get-ChildItem .\_artifacts\*.zip | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||
- ps: Get-ChildItem .\_artifacts\*.exe | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||
- ps: Get-ChildItem .\_artifacts\*.tar.gz | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||
|
||||
only_commits:
|
||||
files:
|
||||
- src/
|
||||
- osx/
|
||||
- gulp/
|
||||
- logo/
|
||||
- setup/
|
||||
- appveyor.yml
|
||||
- build-appveyor.cake
|
||||
870
azure-pipelines.yml
Normal file
@@ -0,0 +1,870 @@
|
||||
# Starter pipeline
|
||||
# Start with a minimal pipeline that you can customize to build and deploy your code.
|
||||
# Add steps that build, run tests, deploy, and more:
|
||||
# https://aka.ms/yaml
|
||||
|
||||
variables:
|
||||
outputFolder: './_output'
|
||||
artifactsFolder: './_artifacts'
|
||||
testsFolder: './_tests'
|
||||
majorVersion: '3.0.0'
|
||||
minorVersion: $[counter('minorVersion', 2000)]
|
||||
radarrVersion: '$(majorVersion).$(minorVersion)'
|
||||
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
|
||||
sentryOrg: 'servarr'
|
||||
sentryUrl: 'https://sentry.servarr.com'
|
||||
dotnetVersion: '3.1.401'
|
||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||
|
||||
trigger:
|
||||
branches:
|
||||
include:
|
||||
- develop
|
||||
- master
|
||||
|
||||
pr:
|
||||
- develop
|
||||
|
||||
stages:
|
||||
- stage: Setup
|
||||
displayName: Setup
|
||||
jobs:
|
||||
- job:
|
||||
displayName: Build Variables
|
||||
pool:
|
||||
vmImage: 'ubuntu-18.04'
|
||||
steps:
|
||||
# Set the build name properly. The 'name' property won't recursively expand so hack here:
|
||||
- bash: echo "##vso[build.updatebuildnumber]$RADARRVERSION"
|
||||
displayName: Set Build Name
|
||||
- bash: |
|
||||
if [[ $BUILD_REASON == "PullRequest" ]]; then
|
||||
git diff origin/develop...HEAD --name-only | grep -E "^(src/|azure-pipelines.yml)"
|
||||
echo $? > not_backend_update
|
||||
else
|
||||
echo 0 > not_backend_update
|
||||
fi
|
||||
cat not_backend_update
|
||||
displayName: Check for Backend File Changes
|
||||
- publish: not_backend_update
|
||||
artifact: not_backend_update
|
||||
displayName: Publish update type
|
||||
- stage: Build_Backend
|
||||
displayName: Build Backend
|
||||
dependsOn: Setup
|
||||
jobs:
|
||||
- job: Backend
|
||||
strategy:
|
||||
matrix:
|
||||
Linux:
|
||||
osName: 'Linux'
|
||||
imageName: 'ubuntu-18.04'
|
||||
Mac:
|
||||
osName: 'Mac'
|
||||
imageName: 'macos-10.14'
|
||||
Windows:
|
||||
osName: 'Windows'
|
||||
imageName: 'windows-2019'
|
||||
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
variables:
|
||||
# Disable stylecop here - linting errors get caught by the analyze task
|
||||
EnableAnalyzers: 'false'
|
||||
steps:
|
||||
- checkout: self
|
||||
submodules: true
|
||||
fetchDepth: 1
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
- bash: ./build.sh --backend
|
||||
displayName: Build Radarr Backend
|
||||
- bash: |
|
||||
find ${OUTPUTFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
|
||||
find ${OUTPUTFOLDER} -depth -empty -type d -exec rm -r "{}" \;
|
||||
find ${TESTSFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
|
||||
find ${TESTSFOLDER} -depth -empty -type d -exec rm -r "{}" \;
|
||||
displayName: Clean up intermediate output
|
||||
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
|
||||
- publish: $(outputFolder)
|
||||
artifact: '$(osName)Backend'
|
||||
displayName: Publish Backend
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
- publish: '$(testsFolder)/netcoreapp3.1/win-x64/publish'
|
||||
artifact: WindowsCoreTests
|
||||
displayName: Publish Windows Test Package
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
- publish: '$(testsFolder)/net462/linux-x64/publish'
|
||||
artifact: LinuxTests
|
||||
displayName: Publish Linux Mono Test Package
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
- publish: '$(testsFolder)/netcoreapp3.1/linux-x64/publish'
|
||||
artifact: LinuxCoreTests
|
||||
displayName: Publish Linux Test Package
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
- publish: '$(testsFolder)/netcoreapp3.1/linux-musl-x64/publish'
|
||||
artifact: LinuxMuslCoreTests
|
||||
displayName: Publish Linux Musl Test Package
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
- publish: '$(testsFolder)/netcoreapp3.1/osx-x64/publish'
|
||||
artifact: MacCoreTests
|
||||
displayName: Publish MacOS Test Package
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
|
||||
- stage: Build_Frontend
|
||||
displayName: Frontend
|
||||
dependsOn: Setup
|
||||
jobs:
|
||||
- job: Build
|
||||
strategy:
|
||||
matrix:
|
||||
Linux:
|
||||
osName: 'Linux'
|
||||
imageName: 'ubuntu-18.04'
|
||||
Mac:
|
||||
osName: 'Mac'
|
||||
imageName: 'macos-10.14'
|
||||
Windows:
|
||||
osName: 'Windows'
|
||||
imageName: 'windows-2019'
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
displayName: Set Node.js version
|
||||
inputs:
|
||||
versionSpec: '10.x'
|
||||
- checkout: self
|
||||
submodules: true
|
||||
fetchDepth: 1
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
key: 'yarn | "$(osName)" | yarn.lock'
|
||||
restoreKeys: |
|
||||
yarn | "$(osName)"
|
||||
yarn
|
||||
path: $(yarnCacheFolder)
|
||||
displayName: Cache Yarn packages
|
||||
- bash: ./build.sh --frontend
|
||||
displayName: Build Radarr Frontend
|
||||
env:
|
||||
FORCE_COLOR: 0
|
||||
YARN_CACHE_FOLDER: $(yarnCacheFolder)
|
||||
- publish: $(outputFolder)
|
||||
artifact: '$(osName)Frontend'
|
||||
displayName: Publish Frontend
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
|
||||
- stage: Installer
|
||||
dependsOn:
|
||||
- Build_Backend
|
||||
- Build_Frontend
|
||||
jobs:
|
||||
- job: Windows_Installer
|
||||
displayName: Create Installer
|
||||
pool:
|
||||
vmImage: 'windows-2019'
|
||||
steps:
|
||||
- checkout: self
|
||||
fetchDepth: 1
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: WindowsBackend
|
||||
targetPath: _output
|
||||
displayName: Fetch Backend
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: WindowsFrontend
|
||||
targetPath: _output
|
||||
displayName: Fetch Frontend
|
||||
- bash: ./build.sh --packages
|
||||
displayName: Create Packages
|
||||
- bash: |
|
||||
setup/inno/ISCC.exe setup/radarr.iss //DFramework=netcoreapp3.1
|
||||
cp setup/output/Radarr.*windows.netcoreapp3.1.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Radarr.${BUILDNAME}.windows-core-x64-installer.exe
|
||||
displayName: Create .NET Core Windows installer
|
||||
- publish: $(Build.ArtifactStagingDirectory)
|
||||
artifact: 'WindowsInstaller'
|
||||
displayName: Publish Installer
|
||||
|
||||
- stage: Packages
|
||||
dependsOn:
|
||||
- Build_Backend
|
||||
- Build_Frontend
|
||||
jobs:
|
||||
- job: Other_Packages
|
||||
displayName: Create Standard Packages
|
||||
pool:
|
||||
vmImage: 'ubuntu-18.04'
|
||||
steps:
|
||||
- checkout: self
|
||||
fetchDepth: 1
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: WindowsBackend
|
||||
targetPath: _output
|
||||
displayName: Fetch Backend
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: WindowsFrontend
|
||||
targetPath: _output
|
||||
displayName: Fetch Frontend
|
||||
- bash: ./build.sh --packages
|
||||
displayName: Create Packages
|
||||
- bash: |
|
||||
find . -name "Radarr" -exec chmod a+x {} \;
|
||||
find . -name "Radarr.Update" -exec chmod a+x {} \;
|
||||
displayName: Set executable bits
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create Windows Core zip
|
||||
inputs:
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).windows-core-x64.zip'
|
||||
archiveType: 'zip'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/windows/netcoreapp3.1
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create MacOS Core app
|
||||
inputs:
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).osx-app-core-x64.zip'
|
||||
archiveType: 'zip'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/macos-app/netcoreapp3.1
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create MacOS Core tar
|
||||
inputs:
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).osx-core-x64.tar.gz'
|
||||
archiveType: 'tar'
|
||||
tarCompression: 'gz'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/macos/netcoreapp3.1
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create Linux Mono tar
|
||||
inputs:
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).linux.tar.gz'
|
||||
archiveType: 'tar'
|
||||
tarCompression: 'gz'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-x64/net462
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create Linux Core tar
|
||||
inputs:
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).linux-core-x64.tar.gz'
|
||||
archiveType: 'tar'
|
||||
tarCompression: 'gz'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-x64/netcoreapp3.1
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create Linux Musl Core tar
|
||||
inputs:
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).linux-musl-core-x64.tar.gz'
|
||||
archiveType: 'tar'
|
||||
tarCompression: 'gz'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-musl-x64/netcoreapp3.1
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create ARM32 Linux Core tar
|
||||
inputs:
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).linux-core-arm.tar.gz'
|
||||
archiveType: 'tar'
|
||||
tarCompression: 'gz'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-arm/netcoreapp3.1
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create ARM64 Linux Core tar
|
||||
inputs:
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).linux-core-arm64.tar.gz'
|
||||
archiveType: 'tar'
|
||||
tarCompression: 'gz'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-arm64/netcoreapp3.1
|
||||
- task: ArchiveFiles@2
|
||||
displayName: Create ARM64 Linux Musl Core tar
|
||||
inputs:
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).linux-musl-core-arm64.tar.gz'
|
||||
archiveType: 'tar'
|
||||
tarCompression: 'gz'
|
||||
includeRootFolder: false
|
||||
rootFolderOrFile: $(artifactsFolder)/linux-musl-arm64/netcoreapp3.1
|
||||
- publish: $(Build.ArtifactStagingDirectory)
|
||||
artifact: 'Packages'
|
||||
displayName: Publish Packages
|
||||
- bash: |
|
||||
echo "Uploading source maps to sentry"
|
||||
curl -sL https://sentry.io/get-cli/ | bash
|
||||
RELEASENAME="${RADARRVERSION}-${BUILD_SOURCEBRANCHNAME}"
|
||||
sentry-cli releases new --finalize -p radarr -p radarr-ui -p radarr-update "${RELEASENAME}"
|
||||
sentry-cli releases -p radarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite
|
||||
sentry-cli releases set-commits --auto "${RELEASENAME}"
|
||||
if [[ ${BUILD_SOURCEBRANCH} == "refs/heads/develop" ]]; then
|
||||
sentry-cli releases deploys "${RELEASENAME}" new -e nightly
|
||||
else
|
||||
sentry-cli releases deploys "${RELEASENAME}" new -e production
|
||||
fi
|
||||
if [ $? -gt 0 ]; then
|
||||
echo "##vso[task.logissue type=warning]Error uploading source maps."
|
||||
fi
|
||||
exit 0
|
||||
displayName: Publish Sentry Source Maps
|
||||
condition: |
|
||||
or
|
||||
(
|
||||
and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')),
|
||||
and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
|
||||
)
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: $(sentryAuthTokenServarr)
|
||||
SENTRY_ORG: $(sentryOrg)
|
||||
SENTRY_URL: $(sentryUrl)
|
||||
|
||||
- stage: Unit_Test
|
||||
displayName: Unit Tests
|
||||
dependsOn: Build_Backend
|
||||
|
||||
jobs:
|
||||
- job: Prepare
|
||||
pool:
|
||||
vmImage: 'ubuntu-18.04'
|
||||
steps:
|
||||
- checkout: none
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: 'not_backend_update'
|
||||
targetPath: '.'
|
||||
- bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
|
||||
name: setVar
|
||||
|
||||
- job: Unit
|
||||
displayName: Unit Native
|
||||
dependsOn: Prepare
|
||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
MacCore:
|
||||
osName: 'Mac'
|
||||
testName: 'MacCore'
|
||||
imageName: 'macos-10.14'
|
||||
WindowsCore:
|
||||
osName: 'Windows'
|
||||
testName: 'WindowsCore'
|
||||
imageName: 'windows-2019'
|
||||
LinuxCore:
|
||||
osName: 'Linux'
|
||||
testName: 'LinuxCore'
|
||||
imageName: 'ubuntu-18.04'
|
||||
pattern: 'Radarr.**.linux-core-x64.tar.gz'
|
||||
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
|
||||
steps:
|
||||
- checkout: none
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Test Artifact
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: '$(testName)Tests'
|
||||
targetPath: $(testsFolder)
|
||||
- bash: |
|
||||
wget https://mediaarea.net/repo/deb/repo-mediaarea_1.0-11_all.deb
|
||||
sudo dpkg -i repo-mediaarea_1.0-11_all.deb
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --allow-unauthenticated libmediainfo-dev libmediainfo0v5 mediainfo
|
||||
displayName: Install mediainfo
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Linux'))
|
||||
- powershell: Set-Service SCardSvr -StartupType Manual
|
||||
displayName: Enable Windows Test Service
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
- bash: |
|
||||
SYMLINK=6_6_0
|
||||
MONOPREFIX=/Library/Frameworks/Mono.framework/Versions/$SYMLINK
|
||||
echo "##vso[task.setvariable variable=MONOPREFIX;]$MONOPREFIX"
|
||||
echo "##vso[task.setvariable variable=PKG_CONFIG_PATH;]$MONOPREFIX/lib/pkgconfig:$MONOPREFIX/share/pkgconfig:$PKG_CONFIG_PATH"
|
||||
echo "##vso[task.setvariable variable=PATH;]$MONOPREFIX/bin:$PATH"
|
||||
displayName: Set Mono Version
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Mac'))
|
||||
- bash: find ${TESTSFOLDER} -name "Radarr.Test.Dummy" -exec chmod a+x {} \;
|
||||
displayName: Make Test Dummy Executable
|
||||
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
|
||||
- bash: |
|
||||
chmod a+x ${TESTSFOLDER}/test.sh
|
||||
${TESTSFOLDER}/test.sh ${OSNAME} Unit Test
|
||||
displayName: Run Tests
|
||||
env:
|
||||
TEST_DIR: $(Build.SourcesDirectory)/_tests
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish Test Results
|
||||
inputs:
|
||||
testResultsFormat: 'NUnit'
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: '$(testName) Unit Tests'
|
||||
failTaskOnFailedTests: true
|
||||
|
||||
- job: Unit_Docker
|
||||
displayName: Unit Docker
|
||||
dependsOn: Prepare
|
||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
strategy:
|
||||
matrix:
|
||||
mono520:
|
||||
testName: 'Mono 5.20'
|
||||
artifactName: LinuxTests
|
||||
containerImage: servarr/testimages:mono-5.20
|
||||
mono610:
|
||||
testName: 'Mono 6.10'
|
||||
artifactName: LinuxTests
|
||||
containerImage: servarr/testimages:mono-6.10
|
||||
mono612:
|
||||
testName: 'Mono 6.12'
|
||||
artifactName: LinuxTests
|
||||
containerImage: servarr/testimages:mono-6.12
|
||||
alpine:
|
||||
testName: 'Musl Net Core'
|
||||
artifactName: LinuxMuslCoreTests
|
||||
containerImage: servarr/testimages:alpine
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-18.04'
|
||||
|
||||
container: $[ variables['containerImage'] ]
|
||||
|
||||
timeoutInMinutes: 10
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
- checkout: none
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Test Artifact
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: $(artifactName)
|
||||
targetPath: $(testsFolder)
|
||||
- bash: find ${TESTSFOLDER} -name "Radarr.Test.Dummy" -exec chmod a+x {} \;
|
||||
displayName: Make Test Dummy Executable
|
||||
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
|
||||
- bash: |
|
||||
chmod a+x ${TESTSFOLDER}/test.sh
|
||||
ls -lR ${TESTSFOLDER}
|
||||
${TESTSFOLDER}/test.sh Linux Unit Test
|
||||
displayName: Run Tests
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish Test Results
|
||||
inputs:
|
||||
testResultsFormat: 'NUnit'
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: '$(testName) Unit Tests'
|
||||
failTaskOnFailedTests: true
|
||||
|
||||
- stage: Integration
|
||||
displayName: Integration
|
||||
dependsOn: Packages
|
||||
|
||||
jobs:
|
||||
- job: Prepare
|
||||
pool:
|
||||
vmImage: 'ubuntu-18.04'
|
||||
steps:
|
||||
- checkout: none
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: 'not_backend_update'
|
||||
targetPath: '.'
|
||||
- bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
|
||||
name: setVar
|
||||
|
||||
- job: Integration_Native
|
||||
displayName: Integration Native
|
||||
dependsOn: Prepare
|
||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
strategy:
|
||||
matrix:
|
||||
MacCore:
|
||||
osName: 'Mac'
|
||||
testName: 'MacCore'
|
||||
imageName: 'macos-10.14'
|
||||
pattern: 'Radarr.**.osx-core-x64.tar.gz'
|
||||
WindowsCore:
|
||||
osName: 'Windows'
|
||||
testName: 'WindowsCore'
|
||||
imageName: 'windows-2019'
|
||||
pattern: 'Radarr.**.windows-core-x64.zip'
|
||||
LinuxCore:
|
||||
osName: 'Linux'
|
||||
testName: 'LinuxCore'
|
||||
imageName: 'ubuntu-18.04'
|
||||
pattern: 'Radarr.**.linux-core-x64.tar.gz'
|
||||
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
|
||||
steps:
|
||||
- bash: |
|
||||
SYMLINK=6_6_0
|
||||
MONOPREFIX=/Library/Frameworks/Mono.framework/Versions/$SYMLINK
|
||||
echo "##vso[task.setvariable variable=MONOPREFIX;]$MONOPREFIX"
|
||||
echo "##vso[task.setvariable variable=PKG_CONFIG_PATH;]$MONOPREFIX/lib/pkgconfig:$MONOPREFIX/share/pkgconfig:$PKG_CONFIG_PATH"
|
||||
echo "##vso[task.setvariable variable=PATH;]$MONOPREFIX/bin:$PATH"
|
||||
displayName: Set Mono Version
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Mac'))
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
- checkout: none
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Test Artifact
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: '$(testName)Tests'
|
||||
targetPath: $(testsFolder)
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Build Artifact
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: Packages
|
||||
itemPattern: '**/$(pattern)'
|
||||
targetPath: $(Build.ArtifactStagingDirectory)
|
||||
- task: ExtractFiles@1
|
||||
inputs:
|
||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
||||
displayName: Extract Package
|
||||
- bash: |
|
||||
mkdir -p ./bin/
|
||||
cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
|
||||
displayName: Move Package Contents
|
||||
- bash: |
|
||||
chmod a+x ${TESTSFOLDER}/test.sh
|
||||
${TESTSFOLDER}/test.sh ${OSNAME} Integration Test
|
||||
displayName: Run Integration Tests
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
testResultsFormat: 'NUnit'
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: '$(testName) Integration Tests'
|
||||
failTaskOnFailedTests: true
|
||||
displayName: Publish Test Results
|
||||
|
||||
- job: Integration_Docker
|
||||
displayName: Integration Docker
|
||||
dependsOn: Prepare
|
||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
strategy:
|
||||
matrix:
|
||||
mono520:
|
||||
testName: 'Mono 5.20'
|
||||
artifactName: LinuxTests
|
||||
containerImage: servarr/testimages:mono-5.20
|
||||
pattern: 'Radarr.**.linux.tar.gz'
|
||||
mono610:
|
||||
testName: 'Mono 6.10'
|
||||
artifactName: LinuxTests
|
||||
containerImage: servarr/testimages:mono-6.10
|
||||
pattern: 'Radarr.**.linux.tar.gz'
|
||||
mono612:
|
||||
testName: 'Mono 6.12'
|
||||
artifactName: LinuxTests
|
||||
containerImage: servarr/testimages:mono-6.12
|
||||
pattern: 'Radarr.**.linux.tar.gz'
|
||||
alpine:
|
||||
testName: 'Musl Net Core'
|
||||
artifactName: LinuxMuslCoreTests
|
||||
containerImage: servarr/testimages:alpine
|
||||
pattern: 'Radarr.**.linux-musl-core-x64.tar.gz'
|
||||
pool:
|
||||
vmImage: 'ubuntu-18.04'
|
||||
|
||||
container: $[ variables['containerImage'] ]
|
||||
|
||||
timeoutInMinutes: 15
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
- checkout: none
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Test Artifact
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: $(artifactName)
|
||||
targetPath: $(testsFolder)
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Build Artifact
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: Packages
|
||||
itemPattern: '**/$(pattern)'
|
||||
targetPath: $(Build.ArtifactStagingDirectory)
|
||||
- task: ExtractFiles@1
|
||||
inputs:
|
||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
||||
displayName: Extract Package
|
||||
- bash: |
|
||||
mkdir -p ./bin/
|
||||
cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
|
||||
displayName: Move Package Contents
|
||||
- bash: |
|
||||
chmod a+x ${TESTSFOLDER}/test.sh
|
||||
${TESTSFOLDER}/test.sh Linux Integration Test
|
||||
displayName: Run Integration Tests
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
testResultsFormat: 'NUnit'
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: '$(testName) Integration Tests'
|
||||
failTaskOnFailedTests: true
|
||||
displayName: Publish Test Results
|
||||
|
||||
- stage: Automation
|
||||
displayName: Automation
|
||||
dependsOn: Packages
|
||||
|
||||
jobs:
|
||||
- job: Automation
|
||||
strategy:
|
||||
matrix:
|
||||
Linux:
|
||||
osName: 'Linux'
|
||||
imageName: 'ubuntu-18.04'
|
||||
pattern: 'Radarr.**.linux-core-x64.tar.gz'
|
||||
failBuild: true
|
||||
Mac:
|
||||
osName: 'Mac'
|
||||
imageName: 'macos-10.14'
|
||||
pattern: 'Radarr.**.osx-core-x64.tar.gz'
|
||||
failBuild: true
|
||||
Windows:
|
||||
osName: 'Windows'
|
||||
imageName: 'windows-2019'
|
||||
pattern: 'Radarr.**.windows-core-x64.zip'
|
||||
failBuild: true
|
||||
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
- checkout: none
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Test Artifact
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: '$(osName)CoreTests'
|
||||
targetPath: $(testsFolder)
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Build Artifact
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: Packages
|
||||
itemPattern: '**/$(pattern)'
|
||||
targetPath: $(Build.ArtifactStagingDirectory)
|
||||
- task: ExtractFiles@1
|
||||
inputs:
|
||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
||||
displayName: Extract Package
|
||||
- bash: |
|
||||
mkdir -p ./bin/
|
||||
cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
|
||||
displayName: Move Package Contents
|
||||
- bash: |
|
||||
chmod a+x ${TESTSFOLDER}/test.sh
|
||||
${TESTSFOLDER}/test.sh ${OSNAME} Automation Test
|
||||
displayName: Run Automation Tests
|
||||
- task: CopyFiles@2
|
||||
displayName: 'Copy Screenshot to: $(Build.ArtifactStagingDirectory)'
|
||||
inputs:
|
||||
SourceFolder: '$(Build.SourcesDirectory)'
|
||||
Contents: |
|
||||
**/*_test_screenshot.png
|
||||
TargetFolder: '$(Build.ArtifactStagingDirectory)/screenshots'
|
||||
- publish: $(Build.ArtifactStagingDirectory)/screenshots
|
||||
artifact: '$(osName)AutomationScreenshots'
|
||||
displayName: Publish Screenshot Bundle
|
||||
condition: and(succeeded(), eq(variables['System.JobAttempt'], '1'))
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
testResultsFormat: 'NUnit'
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: '$(osName) Automation Tests'
|
||||
failTaskOnFailedTests: $(failBuild)
|
||||
displayName: Publish Test Results
|
||||
|
||||
- stage: Analyze
|
||||
dependsOn:
|
||||
- Setup
|
||||
displayName: Analyze
|
||||
|
||||
jobs:
|
||||
- job: Prepare
|
||||
pool:
|
||||
vmImage: 'ubuntu-18.04'
|
||||
steps:
|
||||
- checkout: none
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: 'not_backend_update'
|
||||
targetPath: '.'
|
||||
- bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
|
||||
name: setVar
|
||||
|
||||
- job: Lint_Frontend
|
||||
displayName: Lint Frontend
|
||||
strategy:
|
||||
matrix:
|
||||
Linux:
|
||||
osName: 'Linux'
|
||||
imageName: 'ubuntu-18.04'
|
||||
Windows:
|
||||
osName: 'Windows'
|
||||
imageName: 'windows-2019'
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
displayName: Set Node.js version
|
||||
inputs:
|
||||
versionSpec: '10.x'
|
||||
- checkout: self
|
||||
submodules: true
|
||||
fetchDepth: 1
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
key: 'yarn | "$(osName)" | yarn.lock'
|
||||
restoreKeys: |
|
||||
yarn | "$(osName)"
|
||||
yarn
|
||||
path: $(yarnCacheFolder)
|
||||
displayName: Cache Yarn packages
|
||||
- bash: ./build.sh --lint
|
||||
displayName: Lint Radarr Frontend
|
||||
env:
|
||||
FORCE_COLOR: 0
|
||||
YARN_CACHE_FOLDER: $(yarnCacheFolder)
|
||||
|
||||
- job: Analyze_Frontend
|
||||
displayName: Frontend
|
||||
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
||||
pool:
|
||||
vmImage: windows-2019
|
||||
steps:
|
||||
- checkout: self # Need history for Sonar analysis
|
||||
- task: SonarCloudPrepare@1
|
||||
env:
|
||||
SONAR_SCANNER_OPTS: ''
|
||||
inputs:
|
||||
SonarCloud: 'SonarCloud'
|
||||
organization: 'radarr'
|
||||
scannerMode: 'CLI'
|
||||
configMode: 'manual'
|
||||
cliProjectKey: 'Radarr_Radarr.UI'
|
||||
cliProjectName: 'RadarrUI'
|
||||
cliProjectVersion: '$(radarrVersion)'
|
||||
cliSources: './frontend'
|
||||
- task: SonarCloudAnalyze@1
|
||||
|
||||
- job: Analyze_Backend
|
||||
displayName: Backend
|
||||
dependsOn: Prepare
|
||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
|
||||
variables:
|
||||
disable.coverage.autogenerate: 'true'
|
||||
|
||||
pool:
|
||||
vmImage: windows-2019
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
- checkout: self # Need history for Sonar analysis
|
||||
submodules: true
|
||||
- powershell: Set-Service SCardSvr -StartupType Manual
|
||||
displayName: Enable Windows Test Service
|
||||
- task: SonarCloudPrepare@1
|
||||
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
||||
inputs:
|
||||
SonarCloud: 'SonarCloud'
|
||||
organization: 'radarr'
|
||||
scannerMode: 'MSBuild'
|
||||
projectKey: 'Radarr_Radarr'
|
||||
projectName: 'Radarr'
|
||||
projectVersion: '$(radarrVersion)'
|
||||
extraProperties: |
|
||||
sonar.exclusions=**/obj/**,**/*.dll,**/NzbDrone.Core.Test/Files/**/*,./frontend/**,**/ExternalModules/**,./src/Libraries/**
|
||||
sonar.coverage.exclusions=**/Radarr.Api.V3/**/*,**/NzbDrone.Api/**/*,**/MonoTorrent/**/*,**/Marr.Data/**/*
|
||||
sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml
|
||||
sonar.cs.nunit.reportsPaths=$(Build.SourcesDirectory)/TestResult.xml
|
||||
- bash: |
|
||||
./build.sh --backend -f netcoreapp3.1 -r win-x64
|
||||
TEST_DIR=_tests/netcoreapp3.1/win-x64/publish/ ./test.sh Windows Unit Coverage
|
||||
displayName: Coverage Unit Tests
|
||||
- task: SonarCloudAnalyze@1
|
||||
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
||||
displayName: Publish SonarCloud Results
|
||||
- task: reportgenerator@4
|
||||
displayName: Generate Coverage Report
|
||||
inputs:
|
||||
reports: '$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml'
|
||||
targetdir: '$(Build.SourcesDirectory)/CoverageResults/combined'
|
||||
reporttypes: 'HtmlInline_AzurePipelines;Cobertura;Badges'
|
||||
- task: PublishCodeCoverageResults@1
|
||||
displayName: Publish Coverage Report
|
||||
inputs:
|
||||
codeCoverageTool: 'cobertura'
|
||||
summaryFileLocation: './CoverageResults/combined/Cobertura.xml'
|
||||
reportDirectory: './CoverageResults/combined/'
|
||||
|
||||
- stage: Report_Out
|
||||
dependsOn:
|
||||
- Analyze
|
||||
- Unit_Test
|
||||
- Integration
|
||||
- Automation
|
||||
condition: eq(variables['system.pullrequest.isfork'], false)
|
||||
displayName: Build Status Report
|
||||
jobs:
|
||||
- job:
|
||||
displayName: Discord Notification
|
||||
pool:
|
||||
vmImage: 'windows-2019'
|
||||
steps:
|
||||
- task: DownloadPipelineArtifact@2
|
||||
continueOnError: true
|
||||
displayName: Download Screenshot Artifact
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: 'WindowsAutomationScreenshots'
|
||||
targetPath: $(Build.SourcesDirectory)
|
||||
- checkout: none
|
||||
- powershell: |
|
||||
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/Servarr/AzureDiscordNotify/master/DiscordNotify.ps1'))
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
DISCORDCHANNELID: $(discordChannelId)
|
||||
DISCORDWEBHOOKKEY: $(discordWebhookKey)
|
||||
|
||||
@@ -1,323 +0,0 @@
|
||||
#addin nuget:?package=Cake.Npm
|
||||
#addin nuget:?package=SharpZipLib
|
||||
#addin nuget:?package=Cake.Compression
|
||||
#addin "Cake.FileHelpers"
|
||||
|
||||
// Build variables
|
||||
var outputFolder = "./_output";
|
||||
var outputFolderMono = outputFolder + "_mono";
|
||||
var outputFolderOsx = outputFolder + "_osx";
|
||||
var outputFolderOsxApp = outputFolderOsx + "_app";
|
||||
var testPackageFolder = "./_tests";
|
||||
var testSearchPattern = "*.Test/bin/x86/Release";
|
||||
var sourceFolder = "./src";
|
||||
var solutionFile = sourceFolder + "/NzbDrone.sln";
|
||||
var updateFolder = outputFolder + "/NzbDrone.Update";
|
||||
var updateFolderMono = outputFolderMono + "/NzbDrone.Update";
|
||||
|
||||
// Artifact variables
|
||||
var artifactsFolder = "./_artifacts";
|
||||
var artifactsFolderWindows = artifactsFolder + "/windows";
|
||||
var artifactsFolderLinux = artifactsFolder + "/linux";
|
||||
var artifactsFolderOsx = artifactsFolder + "/osx";
|
||||
var artifactsFolderOsxApp = artifactsFolder + "/osx-app";
|
||||
|
||||
// Utility methods
|
||||
public void RemoveEmptyFolders(string startLocation) {
|
||||
foreach (var directory in System.IO.Directory.GetDirectories(startLocation))
|
||||
{
|
||||
RemoveEmptyFolders(directory);
|
||||
|
||||
if (System.IO.Directory.GetFiles(directory).Length == 0 &&
|
||||
System.IO.Directory.GetDirectories(directory).Length == 0)
|
||||
{
|
||||
DeleteDirectory(directory, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CleanFolder(string path, bool keepConfigFiles) {
|
||||
DeleteFiles(path + "/**/*.transform");
|
||||
|
||||
if (!keepConfigFiles) {
|
||||
DeleteFiles(path + "/**/*.dll.config");
|
||||
}
|
||||
|
||||
DeleteFiles(path + "/**/FluentValidation.resources.dll");
|
||||
DeleteFiles(path + "/**/App.config");
|
||||
|
||||
DeleteFiles(path + "/**/*.less");
|
||||
|
||||
DeleteFiles(path + "/**/*.vshost.exe");
|
||||
|
||||
DeleteFiles(path + "/**/*.dylib");
|
||||
|
||||
RemoveEmptyFolders(path);
|
||||
}
|
||||
|
||||
public void CreateMdbs(string path) {
|
||||
foreach (var file in System.IO.Directory.EnumerateFiles(path, "*.pdb", System.IO.SearchOption.AllDirectories)) {
|
||||
var actualFile = file.Substring(0, file.Length - 4);
|
||||
|
||||
if (FileExists(actualFile + ".exe")) {
|
||||
StartProcess("./tools/pdb2mdb/pdb2mdb.exe", new ProcessSettings()
|
||||
.WithArguments(args => args.Append(actualFile + ".exe")));
|
||||
}
|
||||
|
||||
if (FileExists(actualFile + ".dll")) {
|
||||
StartProcess("./tools/pdb2mdb/pdb2mdb.exe", new ProcessSettings()
|
||||
.WithArguments(args => args.Append(actualFile + ".dll")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build Tasks
|
||||
Task("Compile").Does(() => {
|
||||
// Build
|
||||
if (DirectoryExists(outputFolder)) {
|
||||
DeleteDirectory(outputFolder, true);
|
||||
}
|
||||
|
||||
MSBuild(solutionFile, config =>
|
||||
config.UseToolVersion(MSBuildToolVersion.VS2017)
|
||||
.WithTarget("Clean")
|
||||
.SetVerbosity(Verbosity.Minimal));
|
||||
|
||||
NuGetRestore(solutionFile);
|
||||
|
||||
MSBuild(solutionFile, config =>
|
||||
config.UseToolVersion(MSBuildToolVersion.VS2017)
|
||||
.SetPlatformTarget(PlatformTarget.x86)
|
||||
.SetConfiguration("Release")
|
||||
.WithProperty("AllowedReferenceRelatedFileExtensions", new string[] { ".pdb" })
|
||||
.WithTarget("Build")
|
||||
.SetVerbosity(Verbosity.Minimal));
|
||||
|
||||
CleanFolder(outputFolder, false);
|
||||
|
||||
// Add JsonNet
|
||||
DeleteFiles(outputFolder + "/Newtonsoft.Json.*");
|
||||
CopyFiles(sourceFolder + "/packages/Newtonsoft.Json.*/lib/net35/*.dll", outputFolder);
|
||||
CopyFiles(sourceFolder + "/packages/Newtonsoft.Json.*/lib/net35/*.dll", updateFolder);
|
||||
|
||||
// Remove Mono stuff
|
||||
DeleteFile(outputFolder + "/Mono.Posix.dll");
|
||||
});
|
||||
|
||||
Task("Gulp").Does(() => {
|
||||
NpmInstall(new NpmInstallSettings {
|
||||
LogLevel = NpmLogLevel.Silent,
|
||||
WorkingDirectory = "./",
|
||||
Production = true
|
||||
});
|
||||
|
||||
NpmRunScript("build");
|
||||
});
|
||||
|
||||
Task("PackageMono").Does(() => {
|
||||
// Start mono package
|
||||
if (DirectoryExists(outputFolderMono)) {
|
||||
DeleteDirectory(outputFolderMono, true);
|
||||
}
|
||||
|
||||
CopyDirectory(outputFolder, outputFolderMono);
|
||||
|
||||
// Create MDBs
|
||||
CreateMdbs(outputFolderMono);
|
||||
|
||||
// Remove PDBs
|
||||
DeleteFiles(outputFolderMono + "/**/*.pdb");
|
||||
|
||||
// Remove service helpers
|
||||
DeleteFiles(outputFolderMono + "/ServiceUninstall.*");
|
||||
DeleteFiles(outputFolderMono + "/ServiceInstall.*");
|
||||
|
||||
// Remove native windows binaries
|
||||
DeleteFiles(outputFolderMono + "/sqlite3.*");
|
||||
DeleteFiles(outputFolderMono + "/MediaInfo.*");
|
||||
|
||||
// Adding NzbDrone.Core.dll.config (for dllmap)
|
||||
CopyFile(sourceFolder + "/NzbDrone.Core/NzbDrone.Core.dll.config", outputFolderMono + "/NzbDrone.Core.dll.config");
|
||||
|
||||
// Adding CurlSharp.dll.config (for dllmap)
|
||||
CopyFile(sourceFolder + "/NzbDrone.Common/CurlSharp.dll.config", outputFolderMono + "/CurlSharp.dll.config");
|
||||
|
||||
// Renaming Radarr.Console.exe to Radarr.exe
|
||||
DeleteFiles(outputFolderMono + "/Radarr.exe*");
|
||||
MoveFile(outputFolderMono + "/Radarr.Console.exe", outputFolderMono + "/Radarr.exe");
|
||||
MoveFile(outputFolderMono + "/Radarr.Console.exe.config", outputFolderMono + "/Radarr.exe.config");
|
||||
MoveFile(outputFolderMono + "/Radarr.Console.exe.mdb", outputFolderMono + "/Radarr.exe.mdb");
|
||||
|
||||
// Remove NzbDrone.Windows.*
|
||||
DeleteFiles(outputFolderMono + "/NzbDrone.Windows.*");
|
||||
|
||||
// Adding NzbDrone.Mono to updatePackage
|
||||
CopyFiles(outputFolderMono + "/NzbDrone.Mono.*", updateFolderMono);
|
||||
});
|
||||
|
||||
Task("PackageOsx").Does(() => {
|
||||
// Start osx package
|
||||
if (DirectoryExists(outputFolderOsx)) {
|
||||
DeleteDirectory(outputFolderOsx, true);
|
||||
}
|
||||
|
||||
CopyDirectory(outputFolderMono, outputFolderOsx);
|
||||
|
||||
// Adding sqlite dylibs
|
||||
CopyFiles(sourceFolder + "/Libraries/Sqlite/*.dylib", outputFolderOsx);
|
||||
|
||||
// Adding MediaInfo dylib
|
||||
CopyFiles(sourceFolder + "/Libraries/MediaInfo/*.dylib", outputFolderOsx);
|
||||
|
||||
// Chmod as executable
|
||||
StartProcess(@"C:\cygwin64\bin\chmod.exe", new ProcessSettings()
|
||||
.WithArguments(args => args
|
||||
.Append("+x")
|
||||
.Append(outputFolderOsx + "/Radarr")));
|
||||
|
||||
// Adding Startup script
|
||||
CopyFile("./osx/Radarr", outputFolderOsx + "/Radarr");
|
||||
});
|
||||
|
||||
Task("PackageOsxApp").Does((ctx) => {
|
||||
// Start osx app package
|
||||
if (DirectoryExists(outputFolderOsxApp)) {
|
||||
DeleteDirectory(outputFolderOsxApp, true);
|
||||
}
|
||||
|
||||
CreateDirectory(outputFolderOsxApp);
|
||||
|
||||
// Copy osx package files
|
||||
CopyDirectory("./osx/Radarr.app", outputFolderOsxApp + "/Radarr.app");
|
||||
CopyDirectory(outputFolderOsx, outputFolderOsxApp + "/Radarr.app/Contents/MacOS");
|
||||
|
||||
// Edit version of osx app
|
||||
ctx.ReplaceTextInFiles(outputFolderOsxApp + "/Radarr.app/Contents/Info.plist", "2.0", ctx.EnvironmentVariable("APPVEYOR_BUILD_VERSION") ?? "unknown");
|
||||
});
|
||||
|
||||
Task("PackageTests").Does(() => {
|
||||
// Start tests package
|
||||
if (DirectoryExists(testPackageFolder)) {
|
||||
DeleteDirectory(testPackageFolder, true);
|
||||
}
|
||||
|
||||
CreateDirectory(testPackageFolder);
|
||||
|
||||
// Copy tests
|
||||
CopyFiles(sourceFolder + "/" + testSearchPattern + "/*", testPackageFolder);
|
||||
foreach (var directory in System.IO.Directory.GetDirectories(sourceFolder, "*.Test")) {
|
||||
var releaseDirectory = directory + "/bin/x86/Release";
|
||||
if (DirectoryExists(releaseDirectory)) {
|
||||
foreach (var releaseSubDirectory in System.IO.Directory.GetDirectories(releaseDirectory)) {
|
||||
Information(System.IO.Path.GetDirectoryName(releaseSubDirectory));
|
||||
CopyDirectory(releaseSubDirectory, testPackageFolder + "/" + System.IO.Path.GetFileName(releaseSubDirectory));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Install NUnit.ConsoleRunner
|
||||
NuGetInstall("NUnit.ConsoleRunner", new NuGetInstallSettings {
|
||||
Version = "3.2.0",
|
||||
OutputDirectory = testPackageFolder
|
||||
});
|
||||
|
||||
// Copy dlls
|
||||
CopyFiles(outputFolder + "/*.dll", testPackageFolder);
|
||||
|
||||
// Copy scripts
|
||||
CopyFiles("./*.sh", testPackageFolder);
|
||||
|
||||
// Create MDBs for tests
|
||||
CreateMdbs(testPackageFolder);
|
||||
|
||||
// Remove config
|
||||
DeleteFiles(testPackageFolder + "/*.log.config");
|
||||
|
||||
// Clean
|
||||
CleanFolder(testPackageFolder, true);
|
||||
|
||||
// Adding NzbDrone.Core.dll.config (for dllmap)
|
||||
CopyFile(sourceFolder + "/NzbDrone.Core/NzbDrone.Core.dll.config", testPackageFolder + "/NzbDrone.Core.dll.config");
|
||||
|
||||
// Adding CurlSharp.dll.config (for dllmap)
|
||||
CopyFile(sourceFolder + "/NzbDrone.Common/CurlSharp.dll.config", testPackageFolder + "/CurlSharp.dll.config");
|
||||
|
||||
// Adding CurlSharp libraries
|
||||
CopyFiles(sourceFolder + "/ExternalModules/CurlSharp/libs/i386/*", testPackageFolder);
|
||||
});
|
||||
|
||||
Task("CleanupWindowsPackage").Does(() => {
|
||||
// Remove mono
|
||||
DeleteFiles(outputFolder + "/NzbDrone.Mono.*");
|
||||
|
||||
// Adding NzbDrone.Windows to updatePackage
|
||||
CopyFiles(outputFolder + "/NzbDrone.Windows.*", updateFolder);
|
||||
});
|
||||
|
||||
Task("Build")
|
||||
.IsDependentOn("Compile")
|
||||
.IsDependentOn("Gulp")
|
||||
.IsDependentOn("PackageMono")
|
||||
.IsDependentOn("PackageOsx")
|
||||
.IsDependentOn("PackageOsxApp")
|
||||
.IsDependentOn("PackageTests")
|
||||
.IsDependentOn("CleanupWindowsPackage");
|
||||
|
||||
// Build Artifacts
|
||||
Task("CleanArtifacts").Does(() => {
|
||||
if (DirectoryExists(artifactsFolder)) {
|
||||
DeleteDirectory(artifactsFolder, true);
|
||||
}
|
||||
|
||||
CreateDirectory(artifactsFolder);
|
||||
});
|
||||
|
||||
Task("ArtifactsWindows").Does(() => {
|
||||
CopyDirectory(outputFolder, artifactsFolderWindows + "/Radarr");
|
||||
});
|
||||
|
||||
Task("ArtifactsWindowsInstaller").Does(() => {
|
||||
InnoSetup("./setup/nzbdrone.iss", new InnoSetupSettings {
|
||||
OutputDirectory = artifactsFolder,
|
||||
ToolPath = "./setup/inno/ISCC.exe"
|
||||
});
|
||||
});
|
||||
|
||||
Task("ArtifactsLinux").Does(() => {
|
||||
CopyDirectory(outputFolderMono, artifactsFolderLinux + "/Radarr");
|
||||
});
|
||||
|
||||
Task("ArtifactsOsx").Does(() => {
|
||||
CopyDirectory(outputFolderOsx, artifactsFolderOsx + "/Radarr");
|
||||
});
|
||||
|
||||
Task("ArtifactsOsxApp").Does(() => {
|
||||
CopyDirectory(outputFolderOsxApp, artifactsFolderOsxApp);
|
||||
});
|
||||
|
||||
Task("CompressArtifacts").Does(() => {
|
||||
var prefix = "";
|
||||
|
||||
if (AppVeyor.IsRunningOnAppVeyor) {
|
||||
prefix += AppVeyor.Environment.Repository.Branch.Replace("/", "-") + ".";
|
||||
prefix += AppVeyor.Environment.Build.Version + ".";
|
||||
}
|
||||
|
||||
Zip(artifactsFolderWindows, artifactsFolder + "/Radarr." + prefix + "windows.zip");
|
||||
GZipCompress(artifactsFolderLinux, artifactsFolder + "/Radarr." + prefix + "linux.tar.gz");
|
||||
GZipCompress(artifactsFolderOsx, artifactsFolder + "/Radarr." + prefix + "osx.tar.gz");
|
||||
Zip(artifactsFolderOsxApp, artifactsFolder + "/Radarr." + prefix + "osx-app.zip");
|
||||
});
|
||||
|
||||
Task("Artifacts")
|
||||
.IsDependentOn("CleanArtifacts")
|
||||
.IsDependentOn("ArtifactsWindows")
|
||||
.IsDependentOn("ArtifactsWindowsInstaller")
|
||||
.IsDependentOn("ArtifactsLinux")
|
||||
.IsDependentOn("ArtifactsOsx")
|
||||
.IsDependentOn("ArtifactsOsxApp")
|
||||
.IsDependentOn("CompressArtifacts");
|
||||
|
||||
// Run
|
||||
RunTarget("Build");
|
||||
RunTarget("Artifacts");
|
||||
@@ -1,189 +0,0 @@
|
||||
##########################################################################
|
||||
# This is the Cake bootstrapper script for PowerShell.
|
||||
# This file was downloaded from https://github.com/cake-build/resources
|
||||
# Feel free to change this file to fit your needs.
|
||||
##########################################################################
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
This is a Powershell script to bootstrap a Cake build.
|
||||
.DESCRIPTION
|
||||
This Powershell script will download NuGet if missing, restore NuGet tools (including Cake)
|
||||
and execute your Cake build script with the parameters you provide.
|
||||
.PARAMETER Script
|
||||
The build script to execute.
|
||||
.PARAMETER Target
|
||||
The build script target to run.
|
||||
.PARAMETER Configuration
|
||||
The build configuration to use.
|
||||
.PARAMETER Verbosity
|
||||
Specifies the amount of information to be displayed.
|
||||
.PARAMETER Experimental
|
||||
Tells Cake to use the latest Roslyn release.
|
||||
.PARAMETER WhatIf
|
||||
Performs a dry run of the build script.
|
||||
No tasks will be executed.
|
||||
.PARAMETER Mono
|
||||
Tells Cake to use the Mono scripting engine.
|
||||
.PARAMETER SkipToolPackageRestore
|
||||
Skips restoring of packages.
|
||||
.PARAMETER ScriptArgs
|
||||
Remaining arguments are added here.
|
||||
.LINK
|
||||
http://cakebuild.net
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
[string]$Script = "build-appveyor.cake",
|
||||
[string]$Target = "Default",
|
||||
[ValidateSet("Release", "Debug")]
|
||||
[string]$Configuration = "Release",
|
||||
[ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
|
||||
[string]$Verbosity = "Verbose",
|
||||
[switch]$Experimental,
|
||||
[Alias("DryRun","Noop")]
|
||||
[switch]$WhatIf,
|
||||
[switch]$Mono,
|
||||
[switch]$SkipToolPackageRestore,
|
||||
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
|
||||
[string[]]$ScriptArgs
|
||||
)
|
||||
|
||||
[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null
|
||||
function MD5HashFile([string] $filePath)
|
||||
{
|
||||
if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf))
|
||||
{
|
||||
return $null
|
||||
}
|
||||
|
||||
[System.IO.Stream] $file = $null;
|
||||
[System.Security.Cryptography.MD5] $md5 = $null;
|
||||
try
|
||||
{
|
||||
$md5 = [System.Security.Cryptography.MD5]::Create()
|
||||
$file = [System.IO.File]::OpenRead($filePath)
|
||||
return [System.BitConverter]::ToString($md5.ComputeHash($file))
|
||||
}
|
||||
finally
|
||||
{
|
||||
if ($file -ne $null)
|
||||
{
|
||||
$file.Dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Preparing to run build script..."
|
||||
|
||||
if(!$PSScriptRoot){
|
||||
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
|
||||
}
|
||||
|
||||
$TOOLS_DIR = Join-Path $PSScriptRoot "tools"
|
||||
$NUGET_EXE = Join-Path $TOOLS_DIR "nuget/nuget.exe"
|
||||
$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe"
|
||||
$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
|
||||
$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config"
|
||||
$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum"
|
||||
|
||||
# Should we use mono?
|
||||
$UseMono = "";
|
||||
if($Mono.IsPresent) {
|
||||
Write-Verbose -Message "Using the Mono based scripting engine."
|
||||
$UseMono = "-mono"
|
||||
}
|
||||
|
||||
# Should we use the new Roslyn?
|
||||
$UseExperimental = "";
|
||||
if($Experimental.IsPresent -and !($Mono.IsPresent)) {
|
||||
Write-Verbose -Message "Using experimental version of Roslyn."
|
||||
$UseExperimental = "-experimental"
|
||||
}
|
||||
|
||||
# Is this a dry run?
|
||||
$UseDryRun = "";
|
||||
if($WhatIf.IsPresent) {
|
||||
$UseDryRun = "-dryrun"
|
||||
}
|
||||
|
||||
# Make sure tools folder exists
|
||||
if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
|
||||
Write-Verbose -Message "Creating tools directory..."
|
||||
New-Item -Path $TOOLS_DIR -Type directory | out-null
|
||||
}
|
||||
|
||||
# Make sure that packages.config exist.
|
||||
if (!(Test-Path $PACKAGES_CONFIG)) {
|
||||
Write-Verbose -Message "Downloading packages.config..."
|
||||
try { (New-Object System.Net.WebClient).DownloadFile("http://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch {
|
||||
Throw "Could not download packages.config."
|
||||
}
|
||||
}
|
||||
|
||||
# Try find NuGet.exe in path if not exists
|
||||
if (!(Test-Path $NUGET_EXE)) {
|
||||
Write-Verbose -Message "Trying to find nuget.exe in PATH..."
|
||||
$existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_) }
|
||||
$NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1
|
||||
if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) {
|
||||
Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)."
|
||||
$NUGET_EXE = $NUGET_EXE_IN_PATH.FullName
|
||||
}
|
||||
}
|
||||
|
||||
# Try download NuGet.exe if not exists
|
||||
if (!(Test-Path $NUGET_EXE)) {
|
||||
Write-Verbose -Message "Downloading NuGet.exe..."
|
||||
try {
|
||||
(New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE)
|
||||
} catch {
|
||||
Throw "Could not download NuGet.exe."
|
||||
}
|
||||
}
|
||||
|
||||
# Save nuget.exe path to environment to be available to child processed
|
||||
$ENV:NUGET_EXE = $NUGET_EXE
|
||||
|
||||
# Restore tools from NuGet?
|
||||
if(-Not $SkipToolPackageRestore.IsPresent) {
|
||||
Push-Location
|
||||
Set-Location $TOOLS_DIR
|
||||
|
||||
# Check for changes in packages.config and remove installed tools if true.
|
||||
[string] $md5Hash = MD5HashFile($PACKAGES_CONFIG)
|
||||
if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or
|
||||
($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) {
|
||||
Write-Verbose -Message "Missing or changed package.config hash..."
|
||||
Get-ChildItem -Path $TOOLS_DIR -Recurse -Exclude packages.config |
|
||||
Select -ExpandProperty FullName |
|
||||
Where {$_ -notlike (Join-Path $TOOLS_DIR "pdb2mdb*")} |
|
||||
Where {$_ -notlike (Join-Path $TOOLS_DIR "nuget*")} |
|
||||
sort length -Descending |
|
||||
Remove-Item -Recurse
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Restoring tools from NuGet..."
|
||||
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Throw "An error occured while restoring NuGet tools."
|
||||
}
|
||||
else
|
||||
{
|
||||
$md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII"
|
||||
}
|
||||
Write-Verbose -Message ($NuGetOutput | out-string)
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
# Make sure that Cake has been installed.
|
||||
if (!(Test-Path $CAKE_EXE)) {
|
||||
Throw "Could not find Cake.exe at $CAKE_EXE"
|
||||
}
|
||||
|
||||
# Start Cake
|
||||
Write-Host "Running build script..."
|
||||
Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -target=`"$Target`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental $ScriptArgs"
|
||||
exit $LASTEXITCODE
|
||||
518
build.sh
@@ -1,313 +1,363 @@
|
||||
#! /bin/bash
|
||||
msBuild='/MSBuild/15.0/Bin'
|
||||
outputFolder='./_output'
|
||||
outputFolderMono='./_output_mono'
|
||||
outputFolderOsx='./_output_osx'
|
||||
outputFolderOsxApp='./_output_osx_app'
|
||||
testPackageFolder='./_tests/'
|
||||
testSearchPattern='*.Test/bin/x86/Release'
|
||||
sourceFolder='./src'
|
||||
slnFile=$sourceFolder/NzbDrone.sln
|
||||
updateFolder=$outputFolder/NzbDrone.Update
|
||||
updateFolderMono=$outputFolderMono/NzbDrone.Update
|
||||
set -e
|
||||
|
||||
nuget='tools/nuget/nuget.exe';
|
||||
CheckExitCode()
|
||||
outputFolder='_output'
|
||||
testPackageFolder='_tests'
|
||||
artifactsFolder="_artifacts";
|
||||
|
||||
ProgressStart()
|
||||
{
|
||||
"$@"
|
||||
local status=$?
|
||||
if [ $status -ne 0 ]; then
|
||||
echo "error with $1" >&2
|
||||
exit 1
|
||||
echo "Start '$1'"
|
||||
}
|
||||
|
||||
ProgressEnd()
|
||||
{
|
||||
echo "Finish '$1'"
|
||||
}
|
||||
|
||||
UpdateVersionNumber()
|
||||
{
|
||||
if [ "$RADARRVERSION" != "" ]; then
|
||||
echo "Updating Version Info"
|
||||
sed -i'' -e "s/<AssemblyVersion>[0-9.*]\+<\/AssemblyVersion>/<AssemblyVersion>$RADARRVERSION<\/AssemblyVersion>/g" src/Directory.Build.props
|
||||
sed -i'' -e "s/<AssemblyConfiguration>[\$()A-Za-z-]\+<\/AssemblyConfiguration>/<AssemblyConfiguration>${BUILD_SOURCEBRANCHNAME}<\/AssemblyConfiguration>/g" src/Directory.Build.props
|
||||
sed -i'' -e "s/<string>10.0.0.0<\/string>/<string>$RADARRVERSION<\/string>/g" macOS/Radarr.app/Contents/Info.plist
|
||||
fi
|
||||
return $status
|
||||
}
|
||||
|
||||
CleanFolder()
|
||||
LintUI()
|
||||
{
|
||||
local path=$1
|
||||
local keepConfigFiles=$2
|
||||
ProgressStart 'ESLint'
|
||||
yarn lint
|
||||
ProgressEnd 'ESLint'
|
||||
|
||||
|
||||
find $path -name "*.transform" -exec rm "{}" \;
|
||||
|
||||
if [ $keepConfigFiles != true ] ; then
|
||||
find $path -name "*.dll.config" -exec rm "{}" \;
|
||||
ProgressStart 'Stylelint'
|
||||
if [ "$os" = "windows" ]; then
|
||||
yarn stylelint-windows
|
||||
else
|
||||
yarn stylelint-linux
|
||||
fi
|
||||
|
||||
echo "Removing FluentValidation.Resources files"
|
||||
find $path -name "FluentValidation.resources.dll" -exec rm "{}" \;
|
||||
find $path -name "App.config" -exec rm "{}" \;
|
||||
|
||||
echo "Removing .less files"
|
||||
find $path -name "*.less" -exec rm "{}" \;
|
||||
|
||||
echo "Removing vshost files"
|
||||
find $path -name "*.vshost.exe" -exec rm "{}" \;
|
||||
|
||||
echo "Removing dylib files"
|
||||
find $path -name "*.dylib" -exec rm "{}" \;
|
||||
|
||||
echo "Removing Empty folders"
|
||||
find $path -depth -empty -type d -exec rm -r "{}" \;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AddJsonNet()
|
||||
{
|
||||
rm $outputFolder/Newtonsoft.Json.*
|
||||
cp $sourceFolder/packages/Newtonsoft.Json.*/lib/net35/*.dll $outputFolder
|
||||
cp $sourceFolder/packages/Newtonsoft.Json.*/lib/net35/*.dll $outputFolder/NzbDrone.Update
|
||||
}
|
||||
|
||||
BuildWithMSBuild()
|
||||
{
|
||||
export PATH=$msBuild:$PATH
|
||||
echo $PATH
|
||||
CheckExitCode MSBuild.exe $slnFile //t:Clean //m
|
||||
$nuget restore $slnFile
|
||||
CheckExitCode MSBuild.exe $slnFile //p:Configuration=Release //p:Platform=x86 //t:Build //m //p:AllowedReferenceRelatedFileExtensions=.pdb
|
||||
}
|
||||
|
||||
RestoreNuget()
|
||||
{
|
||||
export MONO_IOMAP=case
|
||||
mono $nuget restore $slnFile
|
||||
}
|
||||
|
||||
CleanWithXbuild()
|
||||
{
|
||||
export MONO_IOMAP=case
|
||||
CheckExitCode msbuild /t:Clean $slnFile
|
||||
}
|
||||
|
||||
BuildWithXbuild()
|
||||
{
|
||||
export MONO_IOMAP=case
|
||||
CheckExitCode msbuild /p:Configuration=Release /p:Platform=x86 /t:Build /p:AllowedReferenceRelatedFileExtensions=.pdb /maxcpucount:3 $slnFile
|
||||
ProgressEnd 'Stylelint'
|
||||
}
|
||||
|
||||
Build()
|
||||
{
|
||||
echo "##teamcity[progressStart 'Build']"
|
||||
ProgressStart 'Build'
|
||||
|
||||
rm -rf $outputFolder
|
||||
rm -rf $testPackageFolder
|
||||
|
||||
if [ $runtime = "dotnet" ] ; then
|
||||
BuildWithMSBuild
|
||||
slnFile=src/Radarr.sln
|
||||
|
||||
if [ $os = "windows" ]; then
|
||||
platform=Windows
|
||||
else
|
||||
CleanWithXbuild
|
||||
RestoreNuget
|
||||
BuildWithXbuild
|
||||
platform=Posix
|
||||
fi
|
||||
|
||||
CleanFolder $outputFolder false
|
||||
dotnet clean $slnFile -c Debug
|
||||
dotnet clean $slnFile -c Release
|
||||
|
||||
AddJsonNet
|
||||
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
|
||||
then
|
||||
dotnet msbuild -restore $slnFile -p:Configuration=Release -p:Platform=$platform -t:PublishAllRids
|
||||
else
|
||||
dotnet msbuild -restore $slnFile -p:Configuration=Release -p:Platform=$platform -p:RuntimeIdentifiers=$RID -t:PublishAllRids
|
||||
fi
|
||||
|
||||
echo "Removing Mono.Posix.dll"
|
||||
rm $outputFolder/Mono.Posix.dll
|
||||
ProgressEnd 'Build'
|
||||
}
|
||||
|
||||
echo "##teamcity[progressFinish 'Build']"
|
||||
YarnInstall()
|
||||
{
|
||||
ProgressStart 'yarn install'
|
||||
yarn install --frozen-lockfile --network-timeout 120000
|
||||
ProgressEnd 'yarn install'
|
||||
}
|
||||
|
||||
RunGulp()
|
||||
{
|
||||
echo "##teamcity[progressStart 'npm install']"
|
||||
npm-cache install npm || CheckExitCode npm install
|
||||
echo "##teamcity[progressFinish 'npm install']"
|
||||
|
||||
echo "##teamcity[progressStart 'Running gulp']"
|
||||
CheckExitCode npm run build
|
||||
echo "##teamcity[progressFinish 'Running gulp']"
|
||||
ProgressStart 'Running gulp'
|
||||
yarn run build --production
|
||||
ProgressEnd 'Running gulp'
|
||||
}
|
||||
|
||||
CreateMdbs()
|
||||
PackageFiles()
|
||||
{
|
||||
local path=$1
|
||||
if [ $runtime = "dotnet" ] ; then
|
||||
local pdbFiles=( $(find $path -name "*.pdb") )
|
||||
for filename in "${pdbFiles[@]}"
|
||||
do
|
||||
if [ -e ${filename%.pdb}.dll ] ; then
|
||||
tools/pdb2mdb/pdb2mdb.exe ${filename%.pdb}.dll
|
||||
fi
|
||||
if [ -e ${filename%.pdb}.exe ] ; then
|
||||
tools/pdb2mdb/pdb2mdb.exe ${filename%.pdb}.exe
|
||||
fi
|
||||
done
|
||||
fi
|
||||
local folder="$1"
|
||||
local framework="$2"
|
||||
local runtime="$3"
|
||||
|
||||
rm -rf $folder
|
||||
mkdir -p $folder
|
||||
cp -r $outputFolder/$framework/$runtime/publish/* $folder
|
||||
cp -r $outputFolder/Radarr.Update/$framework/$runtime/publish $folder/Radarr.Update
|
||||
cp -r $outputFolder/UI $folder
|
||||
|
||||
echo "Adding LICENSE"
|
||||
cp LICENSE $folder
|
||||
}
|
||||
|
||||
PackageMono()
|
||||
PackageLinux()
|
||||
{
|
||||
echo "##teamcity[progressStart 'Creating Mono Package']"
|
||||
rm -rf $outputFolderMono
|
||||
cp -r $outputFolder $outputFolderMono
|
||||
local framework="$1"
|
||||
local runtime="$2"
|
||||
|
||||
echo "Creating MDBs"
|
||||
CreateMdbs $outputFolderMono
|
||||
ProgressStart "Creating $runtime Package for $framework"
|
||||
|
||||
echo "Removing PDBs"
|
||||
find $outputFolderMono -name "*.pdb" -exec rm "{}" \;
|
||||
local folder=$artifactsFolder/$runtime/$framework/Radarr
|
||||
|
||||
PackageFiles "$folder" "$framework" "$runtime"
|
||||
|
||||
echo "Removing Service helpers"
|
||||
rm -f $outputFolderMono/ServiceUninstall.*
|
||||
rm -f $outputFolderMono/ServiceInstall.*
|
||||
rm -f $folder/ServiceUninstall.*
|
||||
rm -f $folder/ServiceInstall.*
|
||||
|
||||
echo "Removing native windows binaries Sqlite, MediaInfo"
|
||||
rm -f $outputFolderMono/sqlite3.*
|
||||
rm -f $outputFolderMono/MediaInfo.*
|
||||
echo "Removing Radarr.Windows"
|
||||
rm $folder/Radarr.Windows.*
|
||||
|
||||
echo "Adding NzbDrone.Core.dll.config (for dllmap)"
|
||||
cp $sourceFolder/NzbDrone.Core/NzbDrone.Core.dll.config $outputFolderMono
|
||||
echo "Adding Radarr.Mono to UpdatePackage"
|
||||
cp $folder/Radarr.Mono.* $folder/Radarr.Update
|
||||
if [ "$framework" = "netcoreapp3.1" ]; then
|
||||
cp $folder/Mono.Posix.NETStandard.* $folder/Radarr.Update
|
||||
cp $folder/libMonoPosixHelper.* $folder/Radarr.Update
|
||||
fi
|
||||
|
||||
echo "Adding CurlSharp.dll.config (for dllmap)"
|
||||
cp $sourceFolder/NzbDrone.Common/CurlSharp.dll.config $outputFolderMono
|
||||
|
||||
echo "Renaming NzbDrone.Console.exe to NzbDrone.exe"
|
||||
rm $outputFolderMono/Radarr.exe*
|
||||
for file in $outputFolderMono/Radarr.Console.exe*; do
|
||||
mv "$file" "${file//.Console/}"
|
||||
done
|
||||
|
||||
echo "Removing NzbDrone.Windows"
|
||||
rm $outputFolderMono/NzbDrone.Windows.*
|
||||
|
||||
echo "Adding NzbDrone.Mono to UpdatePackage"
|
||||
cp $outputFolderMono/NzbDrone.Mono.* $updateFolderMono
|
||||
|
||||
echo "##teamcity[progressFinish 'Creating Mono Package']"
|
||||
ProgressEnd "Creating $runtime Package for $framework"
|
||||
}
|
||||
|
||||
PackageOsx()
|
||||
PackageMacOS()
|
||||
{
|
||||
echo "##teamcity[progressStart 'Creating OS X Package']"
|
||||
rm -rf $outputFolderOsx
|
||||
cp -r $outputFolderMono $outputFolderOsx
|
||||
local framework="$1"
|
||||
|
||||
ProgressStart "Creating MacOS Package for $framework"
|
||||
|
||||
echo "Adding sqlite dylibs"
|
||||
cp $sourceFolder/Libraries/Sqlite/*.dylib $outputFolderOsx
|
||||
local folder=$artifactsFolder/macos/$framework/Radarr
|
||||
|
||||
echo "Adding MediaInfo dylib"
|
||||
cp $sourceFolder/Libraries/MediaInfo/*.dylib $outputFolderOsx
|
||||
PackageFiles "$folder" "$framework" "osx-x64"
|
||||
|
||||
echo "Adding Startup script"
|
||||
cp ./osx/Radarr $outputFolderOsx
|
||||
if [ "$framework" = "net462" ]; then
|
||||
echo "Adding Startup script"
|
||||
cp macOS/Radarr $folder
|
||||
fi
|
||||
|
||||
echo "##teamcity[progressFinish 'Creating OS X Package']"
|
||||
echo "Removing Service helpers"
|
||||
rm -f $folder/ServiceUninstall.*
|
||||
rm -f $folder/ServiceInstall.*
|
||||
|
||||
echo "Removing Radarr.Windows"
|
||||
rm $folder/Radarr.Windows.*
|
||||
|
||||
echo "Adding Radarr.Mono to UpdatePackage"
|
||||
cp $folder/Radarr.Mono.* $folder/Radarr.Update
|
||||
if [ "$framework" = "netcoreapp3.1" ]; then
|
||||
cp $folder/Mono.Posix.NETStandard.* $folder/Radarr.Update
|
||||
cp $folder/libMonoPosixHelper.* $folder/Radarr.Update
|
||||
fi
|
||||
|
||||
ProgressEnd 'Creating MacOS Package'
|
||||
}
|
||||
|
||||
PackageOsxApp()
|
||||
PackageMacOSApp()
|
||||
{
|
||||
echo "##teamcity[progressStart 'Creating OS X App Package']"
|
||||
rm -rf $outputFolderOsxApp
|
||||
mkdir $outputFolderOsxApp
|
||||
local framework="$1"
|
||||
|
||||
ProgressStart "Creating macOS App Package for $framework"
|
||||
|
||||
cp -r ./osx/Radarr.app $outputFolderOsxApp
|
||||
cp -r $outputFolderOsx $outputFolderOsxApp/Radarr.app/Contents/MacOS
|
||||
local folder=$artifactsFolder/macos-app/$framework
|
||||
|
||||
echo "##teamcity[progressFinish 'Creating OS X App Package']"
|
||||
rm -rf $folder
|
||||
mkdir -p $folder
|
||||
cp -r macOS/Radarr.app $folder
|
||||
mkdir -p $folder/Radarr.app/Contents/MacOS
|
||||
|
||||
echo "Copying Binaries"
|
||||
cp -r $artifactsFolder/macos/$framework/Radarr/* $folder/Radarr.app/Contents/MacOS
|
||||
|
||||
echo "Removing Update Folder"
|
||||
rm -r $folder/Radarr.app/Contents/MacOS/Radarr.Update
|
||||
|
||||
ProgressEnd 'Creating macOS App Package'
|
||||
}
|
||||
|
||||
PackageWindows()
|
||||
{
|
||||
local framework="$1"
|
||||
|
||||
ProgressStart "Creating Windows Package for $framework"
|
||||
|
||||
local folder=$artifactsFolder/windows/$framework/Radarr
|
||||
|
||||
PackageFiles "$folder" "$framework" "win-x64"
|
||||
|
||||
echo "Removing Radarr.Mono"
|
||||
rm -f $folder/Radarr.Mono.*
|
||||
rm -f $folder/Mono.Posix.NETStandard.*
|
||||
rm -f $folder/libMonoPosixHelper.*
|
||||
|
||||
echo "Adding Radarr.Windows to UpdatePackage"
|
||||
cp $folder/Radarr.Windows.* $folder/Radarr.Update
|
||||
|
||||
ProgressEnd 'Creating Windows Package'
|
||||
}
|
||||
|
||||
Package()
|
||||
{
|
||||
local framework="$1"
|
||||
local runtime="$2"
|
||||
local SPLIT
|
||||
|
||||
IFS='-' read -ra SPLIT <<< "$runtime"
|
||||
|
||||
case "${SPLIT[0]}" in
|
||||
linux)
|
||||
PackageLinux "$framework" "$runtime"
|
||||
;;
|
||||
win)
|
||||
PackageWindows "$framework"
|
||||
;;
|
||||
osx)
|
||||
PackageMacOS "$framework"
|
||||
PackageMacOSApp "$framework"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
PackageTests()
|
||||
{
|
||||
echo "Packaging Tests"
|
||||
echo "##teamcity[progressStart 'Creating Test Package']"
|
||||
rm -rf $testPackageFolder
|
||||
mkdir $testPackageFolder
|
||||
local framework="$1"
|
||||
local runtime="$2"
|
||||
|
||||
find $sourceFolder -path $testSearchPattern -exec cp -r -u -T "{}" $testPackageFolder \;
|
||||
cp test.sh "$testPackageFolder/$framework/$runtime/publish"
|
||||
|
||||
if [ $runtime = "dotnet" ] ; then
|
||||
$nuget install NUnit.Runners -Version 3.9.0 -Output $testPackageFolder
|
||||
else
|
||||
mono $nuget install NUnit.Runners -Version 3.9.0 -Output $testPackageFolder
|
||||
rm -f $testPackageFolder/$framework/$runtime/*.log.config
|
||||
|
||||
# geckodriver.exe isn't copied by dotnet publish
|
||||
if [ "$runtime" = "win-x64" ];
|
||||
then
|
||||
curl -Lso gecko.zip "https://github.com/mozilla/geckodriver/releases/download/v0.27.0/geckodriver-v0.27.0-win64.zip"
|
||||
unzip -o gecko.zip
|
||||
cp geckodriver.exe "$testPackageFolder/$framework/win-x64/publish"
|
||||
fi
|
||||
|
||||
cp $outputFolder/*.dll $testPackageFolder
|
||||
cp ./*.sh $testPackageFolder
|
||||
|
||||
echo "Creating MDBs for tests"
|
||||
CreateMdbs $testPackageFolder
|
||||
|
||||
rm -f $testPackageFolder/*.log.config
|
||||
|
||||
CleanFolder $testPackageFolder true
|
||||
|
||||
echo "Adding NzbDrone.Core.dll.config (for dllmap)"
|
||||
cp $sourceFolder/NzbDrone.Core/NzbDrone.Core.dll.config $testPackageFolder
|
||||
|
||||
echo "Adding CurlSharp.dll.config (for dllmap)"
|
||||
cp $sourceFolder/NzbDrone.Common/CurlSharp.dll.config $testPackageFolder
|
||||
|
||||
echo "Copying CurlSharp libraries"
|
||||
cp $sourceFolder/ExternalModules/CurlSharp/libs/i386/* $testPackageFolder
|
||||
|
||||
echo "##teamcity[progressFinish 'Creating Test Package']"
|
||||
}
|
||||
|
||||
CleanupWindowsPackage()
|
||||
{
|
||||
echo "Removing NzbDrone.Mono"
|
||||
rm -f $outputFolder/NzbDrone.Mono.*
|
||||
|
||||
echo "Adding NzbDrone.Windows to UpdatePackage"
|
||||
cp $outputFolder/NzbDrone.Windows.* $updateFolder
|
||||
ProgressEnd 'Creating Test Package'
|
||||
}
|
||||
|
||||
# Use mono or .net depending on OS
|
||||
case "$(uname -s)" in
|
||||
CYGWIN*|MINGW32*|MINGW64*|MSYS*)
|
||||
# on windows, use dotnet
|
||||
runtime="dotnet"
|
||||
vsLoc=$(./vswhere.exe -property installationPath)
|
||||
vsLoc=$(echo "/$vsLoc" | sed -e 's/\\/\//g' -e 's/://')
|
||||
msBuild="$vsLoc$msBuild"
|
||||
os="windows"
|
||||
;;
|
||||
*)
|
||||
# otherwise use mono
|
||||
runtime="mono"
|
||||
os="posix"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ $# -eq 0 ]
|
||||
then
|
||||
POSITIONAL=()
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "No arguments provided, building everything"
|
||||
BACKEND=YES
|
||||
FRONTEND=YES
|
||||
PACKAGES=YES
|
||||
LINT=YES
|
||||
fi
|
||||
|
||||
while [[ $# -gt 0 ]]
|
||||
do
|
||||
key="$1"
|
||||
|
||||
case $key in
|
||||
--backend)
|
||||
BACKEND=YES
|
||||
shift # past argument
|
||||
;;
|
||||
-r|--runtime)
|
||||
RID="$2"
|
||||
shift # past argument
|
||||
shift # past value
|
||||
;;
|
||||
-f|--framework)
|
||||
FRAMEWORK="$2"
|
||||
shift # past argument
|
||||
shift # past value
|
||||
;;
|
||||
--frontend)
|
||||
FRONTEND=YES
|
||||
shift # past argument
|
||||
;;
|
||||
--packages)
|
||||
PACKAGES=YES
|
||||
shift # past argument
|
||||
;;
|
||||
--lint)
|
||||
LINT=YES
|
||||
shift # past argument
|
||||
;;
|
||||
--all)
|
||||
BACKEND=YES
|
||||
FRONTEND=YES
|
||||
PACKAGES=YES
|
||||
LINT=YES
|
||||
shift # past argument
|
||||
;;
|
||||
*) # unknown option
|
||||
POSITIONAL+=("$1") # save it in an array for later
|
||||
shift # past argument
|
||||
;;
|
||||
esac
|
||||
done
|
||||
set -- "${POSITIONAL[@]}" # restore positional parameters
|
||||
|
||||
if [ "$BACKEND" = "YES" ];
|
||||
then
|
||||
UpdateVersionNumber
|
||||
Build
|
||||
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
|
||||
then
|
||||
PackageTests "netcoreapp3.1" "win-x64"
|
||||
PackageTests "netcoreapp3.1" "linux-x64"
|
||||
PackageTests "netcoreapp3.1" "linux-musl-x64"
|
||||
PackageTests "netcoreapp3.1" "osx-x64"
|
||||
PackageTests "net462" "linux-x64"
|
||||
else
|
||||
PackageTests "$FRAMEWORK" "$RID"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$FRONTEND" = "YES" ];
|
||||
then
|
||||
YarnInstall
|
||||
RunGulp
|
||||
PackageMono
|
||||
PackageOsx
|
||||
PackageOsxApp
|
||||
PackageTests
|
||||
CleanupWindowsPackage
|
||||
fi
|
||||
|
||||
if [ "$1" = "CleanXbuild" ]
|
||||
then rm -rf $outputFolder
|
||||
CleanWithXbuild
|
||||
if [ "$LINT" = "YES" ];
|
||||
then
|
||||
if [ -z "$FRONTEND" ];
|
||||
then
|
||||
YarnInstall
|
||||
fi
|
||||
|
||||
LintUI
|
||||
fi
|
||||
|
||||
if [ "$1" = "NugetMono" ]
|
||||
then rm -rf $outputFolder
|
||||
RestoreNuget
|
||||
fi
|
||||
if [ "$PACKAGES" = "YES" ];
|
||||
then
|
||||
UpdateVersionNumber
|
||||
|
||||
if [ "$1" = "Build" ]
|
||||
then BuildWithXbuild
|
||||
CleanFolder $outputFolder false
|
||||
AddJsonNet
|
||||
rm $outputFolder/Mono.Posix.dll
|
||||
fi
|
||||
|
||||
if [ "$1" = "Gulp" ]
|
||||
then RunGulp
|
||||
fi
|
||||
|
||||
if [ "$1" = "Package" ]
|
||||
then PackageMono
|
||||
PackageOsx
|
||||
PackageOsxApp
|
||||
PackageTests
|
||||
CleanupWindowsPackage
|
||||
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
|
||||
then
|
||||
Package "netcoreapp3.1" "win-x64"
|
||||
Package "netcoreapp3.1" "linux-x64"
|
||||
Package "netcoreapp3.1" "linux-musl-x64"
|
||||
Package "netcoreapp3.1" "linux-arm64"
|
||||
Package "netcoreapp3.1" "linux-musl-arm64"
|
||||
Package "netcoreapp3.1" "linux-arm"
|
||||
Package "netcoreapp3.1" "osx-x64"
|
||||
Package "net462" "linux-x64"
|
||||
else
|
||||
Package "$FRAMEWORK" "$RID"
|
||||
fi
|
||||
fi
|
||||
|
||||
48
debian/copyright
vendored
@@ -1,24 +1,24 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: nzbdrone
|
||||
Source: https://github.com/Sonarr/Sonarr
|
||||
|
||||
Files: *
|
||||
Copyright: 2010-2016 Sonarr <hello@sonarr.tv>
|
||||
|
||||
License: GPL-3.0+
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
.
|
||||
This package is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General
|
||||
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: nzbdrone
|
||||
Source: https://github.com/Sonarr/Sonarr
|
||||
|
||||
Files: *
|
||||
Copyright: 2010-2016 Sonarr <hello@sonarr.tv>
|
||||
|
||||
License: GPL-3.0+
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
.
|
||||
This package is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General
|
||||
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
|
||||
|
||||
2
debian/install
vendored
@@ -1 +1 @@
|
||||
nzbdrone_bin/* opt/NzbDrone
|
||||
nzbdrone_bin/* opt/NzbDrone
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
if [ -z "$CIRCLE_PULL_REQUEST" ]; then
|
||||
echo "We are building a normal branch, deploying as such..."
|
||||
curl "http://pr.radarr.video:4466/deploy?url=https%3A%2F%2F${CIRCLE_BUILD_NUM}-77323220-gh.circle-artifacts.com%2F0%2Fartifacts%2FRadarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.linux.tar.gz&b=branch&name=${CIRCLE_BRANCH}"
|
||||
else
|
||||
echo "We are building a pr, deploying as such..."
|
||||
curl "http://pr.radarr.video:4466/deploy?url=https%3A%2F%2F${CIRCLE_BUILD_NUM}-77323220-gh.circle-artifacts.com%2F0%2Fartifacts%2FRadarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.linux.tar.gz&b=pr&name=${CIRCLE_PR_NUMBER}"
|
||||
fi
|
||||
25
frontend/.csscomb.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"remove-empty-rulesets": true,
|
||||
"always-semicolon": true,
|
||||
"color-case": "lower",
|
||||
"block-indent": " ",
|
||||
"color-shorthand": false,
|
||||
"element-case": "lower",
|
||||
"eof-newline": true,
|
||||
"leading-zero": true,
|
||||
"quotes": "double",
|
||||
"sort-order-fallback": "abc",
|
||||
"space-before-colon": "",
|
||||
"space-after-colon": " ",
|
||||
"space-before-combinator": " ",
|
||||
"space-after-combinator": " ",
|
||||
"space-between-declarations": "\n",
|
||||
"space-before-opening-brace": " ",
|
||||
"space-after-opening-brace": "\n",
|
||||
"space-after-selector-delimiter": " ",
|
||||
"space-before-selector-delimiter": "",
|
||||
"space-before-closing-brace": "\n",
|
||||
"strip-spaces": true,
|
||||
"tab-size": true,
|
||||
"unitless-zero": false
|
||||
}
|
||||
335
frontend/.esformatter
Normal file
@@ -0,0 +1,335 @@
|
||||
{
|
||||
"indent": {
|
||||
"value": " ",
|
||||
"FunctionExpression": 1,
|
||||
"ArrayExpression": 1,
|
||||
"ObjectExpression": 1
|
||||
},
|
||||
"lineBreak": {
|
||||
"value": "\n",
|
||||
|
||||
"before": {
|
||||
"ArrayPatternClosing": 0,
|
||||
"ArrayPatternComma": 0,
|
||||
"ArrayPatternOpening": 0,
|
||||
"ArrowFunctionExpressionArrow": 0,
|
||||
"ArrowFunctionExpressionClosingBrace": ">=1",
|
||||
"ArrowFunctionExpressionOpeningBrace": 0,
|
||||
"AssignmentExpression": ">=1",
|
||||
"AssignmentOperator": 0,
|
||||
"BlockStatement": 0,
|
||||
"BreakKeyword": ">=1",
|
||||
"CallExpression": -1,
|
||||
"CallExpressionClosingParentheses": -1,
|
||||
"CallExpressionOpeningParentheses": 0,
|
||||
"CatchClosingBrace": ">=1",
|
||||
"CatchKeyword": 0,
|
||||
"CatchOpeningBrace": 0,
|
||||
"ClassDeclaration": ">=1",
|
||||
"ClassDeclarationClosingBrace": ">=1",
|
||||
"ClassDeclarationOpeningBrace": 0,
|
||||
"ConditionalExpression": ">=1",
|
||||
"DeleteOperator": ">=1",
|
||||
"DoWhileStatement": ">=1",
|
||||
"DoWhileStatementClosingBrace": ">=1",
|
||||
"DoWhileStatementOpeningBrace": 0,
|
||||
"ElseIfStatement": 0,
|
||||
"ElseIfStatementClosingBrace": ">=1",
|
||||
"ElseIfStatementOpeningBrace": 0,
|
||||
"ElseStatement": 0,
|
||||
"ElseStatementClosingBrace": ">=1",
|
||||
"ElseStatementOpeningBrace": 0,
|
||||
"EmptyStatement": -1,
|
||||
"EndOfFile": -1,
|
||||
"FinallyClosingBrace": ">=1",
|
||||
"FinallyKeyword": -1,
|
||||
"FinallyOpeningBrace": 0,
|
||||
"ForInStatement": ">=1",
|
||||
"ForInStatementClosingBrace": ">=1",
|
||||
"ForInStatementExpressionClosing": 0,
|
||||
"ForInStatementExpressionOpening": 0,
|
||||
"ForInStatementOpeningBrace": 0,
|
||||
"ForStatement": ">=1",
|
||||
"ForStatementClosingBrace": ">=1",
|
||||
"ForStatementExpressionClosing": "<2",
|
||||
"ForStatementExpressionOpening": 0,
|
||||
"ForStatementOpeningBrace": 0,
|
||||
"FunctionDeclaration": ">=1",
|
||||
"FunctionDeclarationClosingBrace": ">=1",
|
||||
"FunctionDeclarationOpeningBrace": 0,
|
||||
"FunctionExpression": 0,
|
||||
"FunctionExpressionClosingBrace": 1,
|
||||
"FunctionExpressionOpeningBrace":0,
|
||||
"IIFEClosingParentheses": 0,
|
||||
"IfStatement": ">=1",
|
||||
"IfStatementClosingBrace": ">=1",
|
||||
"IfStatementOpeningBrace": 0,
|
||||
"LogicalExpression": -1,
|
||||
"MemberExpressionClosing": 0,
|
||||
"MemberExpressionOpening": 0,
|
||||
"MemberExpressionPeriod": -1,
|
||||
"MethodDefinition": ">=1",
|
||||
"ObjectExpressionClosingBrace": "<=1",
|
||||
"ObjectPatternClosingBrace": 0,
|
||||
"ObjectPatternComma": 0,
|
||||
"ObjectPatternOpeningBrace": 0,
|
||||
"ParameterDefault": 0,
|
||||
"Property": "<=2",
|
||||
"PropertyValue": 0,
|
||||
"ReturnStatement": -1,
|
||||
"SwitchClosingBrace": ">=1",
|
||||
"SwitchOpeningBrace": 0,
|
||||
"ThisExpression": -1,
|
||||
"ThrowStatement": ">=1",
|
||||
"TryClosingBrace": ">=1",
|
||||
"TryKeyword": -1,
|
||||
"TryOpeningBrace": 0,
|
||||
"VariableDeclaration": ">=1",
|
||||
"VariableDeclarationSemiColon": 0,
|
||||
"VariableDeclarationWithoutInit": ">=1",
|
||||
"VariableName": ">=1",
|
||||
"VariableValue": 0,
|
||||
"WhileStatement": ">=1",
|
||||
"WhileStatementClosingBrace": ">=1",
|
||||
"WhileStatementOpeningBrace": 0
|
||||
},
|
||||
|
||||
"after": {
|
||||
"ArrayPatternClosing": 0,
|
||||
"ArrayPatternComma": 0,
|
||||
"ArrayPatternOpening": 0,
|
||||
"ArrowFunctionExpressionArrow": 0,
|
||||
"ArrowFunctionExpressionClosingBrace": -1,
|
||||
"ArrowFunctionExpressionOpeningBrace": ">=1",
|
||||
"AssignmentExpression": ">=1",
|
||||
"AssignmentOperator": 0,
|
||||
"BlockStatement": 0,
|
||||
"BreakKeyword": -1,
|
||||
"CallExpression": -1,
|
||||
"CallExpressionClosingParentheses": -1,
|
||||
"CallExpressionOpeningParentheses": -1,
|
||||
"CatchClosingBrace": ">=0",
|
||||
"CatchKeyword": 0,
|
||||
"CatchOpeningBrace": ">=1",
|
||||
"ClassDeclaration": ">=1",
|
||||
"ClassDeclarationClosingBrace": ">=1",
|
||||
"ClassDeclarationOpeningBrace": ">=1",
|
||||
"ConditionalExpression": ">=1",
|
||||
"DeleteOperator": ">=1",
|
||||
"DoWhileStatement": ">=1",
|
||||
"DoWhileStatementClosingBrace": 0,
|
||||
"DoWhileStatementOpeningBrace": ">=1",
|
||||
"ElseIfStatement": ">=1",
|
||||
"ElseIfStatementClosingBrace": ">=1",
|
||||
"ElseIfStatementOpeningBrace": ">=1",
|
||||
"ElseStatement": ">=1",
|
||||
"ElseStatementClosingBrace": ">=1",
|
||||
"ElseStatementOpeningBrace": ">=1",
|
||||
"EmptyStatement": -1,
|
||||
"FinallyClosingBrace": ">=1",
|
||||
"FinallyKeyword": -1,
|
||||
"FinallyOpeningBrace": ">=1",
|
||||
"ForInStatement": ">=1",
|
||||
"ForInStatementClosingBrace": ">=1",
|
||||
"ForInStatementExpressionClosing": -1,
|
||||
"ForInStatementExpressionOpening": "<2",
|
||||
"ForInStatementOpeningBrace": ">=1",
|
||||
"ForStatement": ">=1",
|
||||
"ForStatementClosingBrace": ">=1",
|
||||
"ForStatementExpressionClosing": -1,
|
||||
"ForStatementExpressionOpening": "<2",
|
||||
"ForStatementOpeningBrace": ">=1",
|
||||
"FunctionDeclaration": ">=1",
|
||||
"FunctionDeclarationClosingBrace": ">=1",
|
||||
"FunctionDeclarationOpeningBrace": ">=1",
|
||||
"FunctionExpression": 0,
|
||||
"FunctionExpressionClosingBrace": -1,
|
||||
"FunctionExpressionOpeningBrace": 1,
|
||||
"IIFEOpeningParentheses": 0,
|
||||
"IfStatement": ">=1",
|
||||
"IfStatementClosingBrace": ">=1",
|
||||
"IfStatementOpeningBrace": ">=1",
|
||||
"LogicalExpression": -1,
|
||||
"MemberExpressionClosing": 0,
|
||||
"MemberExpressionOpening": 0,
|
||||
"MemberExpressionPeriod": 0,
|
||||
"MethodDefinition": ">=1",
|
||||
"ObjectExpressionOpeningBrace": "<=1",
|
||||
"ObjectPatternClosingBrace": 0,
|
||||
"ObjectPatternComma": 0,
|
||||
"ObjectPatternOpeningBrace": 0,
|
||||
"ParameterDefault": 0,
|
||||
"Property": -1,
|
||||
"PropertyName": 0,
|
||||
"ReturnStatement": -1,
|
||||
"SwitchCaseColon": ">=1",
|
||||
"SwitchClosingBrace": ">=1",
|
||||
"SwitchOpeningBrace": ">=1",
|
||||
"ThisExpression": 0,
|
||||
"ThrowStatement": ">=1",
|
||||
"TryClosingBrace": 0,
|
||||
"TryKeyword": -1,
|
||||
"TryOpeningBrace": ">=1",
|
||||
"VariableDeclaration": ">=1",
|
||||
"VariableDeclarationSemiColon": ">=1",
|
||||
"VariableValue": -1,
|
||||
"WhileStatement": ">=1",
|
||||
"WhileStatementClosingBrace": ">=1",
|
||||
"WhileStatementOpeningBrace": ">=1"
|
||||
}
|
||||
},
|
||||
"whiteSpace": {
|
||||
"value": " ",
|
||||
"removeTrailing": 1,
|
||||
"before": {
|
||||
"ArgumentComma": 0,
|
||||
"ArgumentList": 0,
|
||||
"ArgumentListArrayExpression": 0,
|
||||
"ArgumentListFunctionExpression": 1,
|
||||
"ArgumentListObjectExpression": 0,
|
||||
"ArrayExpressionClosing": 0,
|
||||
"ArrayExpressionComma": 0,
|
||||
"ArrayExpressionOpening": 1,
|
||||
"AssignmentOperator": 1,
|
||||
"BinaryExpression": 0,
|
||||
"BinaryExpressionOperator": 1,
|
||||
"BlockComment": 1,
|
||||
"CallExpression": 1,
|
||||
"CatchClosingBrace": 1,
|
||||
"CatchKeyword": 1,
|
||||
"CatchOpeningBrace": 1,
|
||||
"CatchParameterList": 0,
|
||||
"CommaOperator": 0,
|
||||
"ConditionalExpressionAlternate": 1,
|
||||
"ConditionalExpressionConsequent": 1,
|
||||
"DoWhileStatementClosingBrace": 1,
|
||||
"DoWhileStatementConditional": 1,
|
||||
"DoWhileStatementOpeningBrace": 1,
|
||||
"ElseIfStatementClosingBrace": 1,
|
||||
"ElseIfStatementOpeningBrace": 1,
|
||||
"ElseStatementClosingBrace": 1,
|
||||
"ElseStatementOpeningBrace": 1,
|
||||
"EmptyStatement": 0,
|
||||
"ExpressionClosingParentheses": 0,
|
||||
"FinallyClosingBrace": 1,
|
||||
"FinallyKeyword": -1,
|
||||
"FinallyOpeningBrace": 1,
|
||||
"ForInStatement": 1,
|
||||
"ForInStatementClosingBrace": 1,
|
||||
"ForInStatementExpressionClosing": 0,
|
||||
"ForInStatementExpressionOpening": 1,
|
||||
"ForInStatementOpeningBrace": 1,
|
||||
"ForStatement": 1,
|
||||
"ForStatementClosingBrace": 1,
|
||||
"ForStatementExpressionClosing": 0,
|
||||
"ForStatementExpressionOpening": 1,
|
||||
"ForStatementOpeningBrace": 1,
|
||||
"ForStatementSemicolon": 0,
|
||||
"FunctionDeclarationClosingBrace": 1,
|
||||
"FunctionDeclarationOpeningBrace": 1,
|
||||
"FunctionExpressionClosingBrace": 1,
|
||||
"FunctionExpressionOpeningBrace": 1,
|
||||
"IfStatementClosingBrace": 1,
|
||||
"IfStatementConditionalClosing": 0,
|
||||
"IfStatementConditionalOpening": 1,
|
||||
"IfStatementOpeningBrace": 1,
|
||||
"LineComment": 1,
|
||||
"LogicalExpressionOperator": 1,
|
||||
"MemberExpressionClosing": 0,
|
||||
"ObjectExpressionClosingBrace": 1,
|
||||
"ParameterComma": 0,
|
||||
"ParameterList": 0,
|
||||
"Property": 1,
|
||||
"PropertyName": 1,
|
||||
"PropertyValue": 1,
|
||||
"SwitchDiscriminantClosing": 0,
|
||||
"SwitchDiscriminantOpening": 1,
|
||||
"ThrowKeyword": 1,
|
||||
"TryClosingBrace": 1,
|
||||
"TryKeyword": -1,
|
||||
"TryOpeningBrace": 1,
|
||||
"UnaryExpressionOperator": 0,
|
||||
"VariableName": 1,
|
||||
"VariableValue": 1,
|
||||
"WhileStatementClosingBrace": 1,
|
||||
"WhileStatementConditionalClosing": 0,
|
||||
"WhileStatementConditionalOpening": 1,
|
||||
"WhileStatementOpeningBrace": 1
|
||||
},
|
||||
"after": {
|
||||
"ArgumentComma": 1,
|
||||
"ArgumentList": 0,
|
||||
"ArgumentListArrayExpression": 1,
|
||||
"ArgumentListFunctionExpression": 1,
|
||||
"ArgumentListObjectExpression": 0,
|
||||
"ArrayExpressionClosing": 0,
|
||||
"ArrayExpressionComma": 1,
|
||||
"ArrayExpressionOpening": 0,
|
||||
"AssignmentOperator": 1,
|
||||
"BinaryExpression": 0,
|
||||
"BinaryExpressionOperator": 1,
|
||||
"BlockComment": 1,
|
||||
"CallExpression": 0,
|
||||
"CatchClosingBrace": 1,
|
||||
"CatchKeyword": 1,
|
||||
"CatchOpeningBrace": 1,
|
||||
"CatchParameterList": 0,
|
||||
"CommaOperator": 1,
|
||||
"ConditionalExpressionConsequent": 1,
|
||||
"ConditionalExpressionTest": 1,
|
||||
"DoWhileStatementBody": 1,
|
||||
"DoWhileStatementClosingBrace": 1,
|
||||
"DoWhileStatementOpeningBrace": 1,
|
||||
"ElseIfStatementClosingBrace": 1,
|
||||
"ElseIfStatementOpeningBrace": 1,
|
||||
"ElseStatementClosingBrace": 1,
|
||||
"ElseStatementOpeningBrace": 1,
|
||||
"EmptyStatement": 0,
|
||||
"ExpressionOpeningParentheses": 0,
|
||||
"FinallyClosingBrace": 1,
|
||||
"FinallyKeyword": -1,
|
||||
"FinallyOpeningBrace": 1,
|
||||
"ForInStatement": 1,
|
||||
"ForInStatementClosingBrace": 1,
|
||||
"ForInStatementExpressionClosing": 1,
|
||||
"ForInStatementExpressionOpening": 0,
|
||||
"ForInStatementOpeningBrace": 1,
|
||||
"ForStatement": 1,
|
||||
"ForStatementClosingBrace": 1,
|
||||
"ForStatementExpressionClosing": 1,
|
||||
"ForStatementExpressionOpening": 0,
|
||||
"ForStatementOpeningBrace": 1,
|
||||
"ForStatementSemicolon": 1,
|
||||
"FunctionDeclarationClosingBrace": 0,
|
||||
"FunctionDeclarationOpeningBrace": 0,
|
||||
"FunctionExpressionClosingBrace": 0,
|
||||
"FunctionExpressionOpeningBrace": 0,
|
||||
"FunctionName": 0,
|
||||
"FunctionReservedWord": 0,
|
||||
"IfStatementClosingBrace": 1,
|
||||
"IfStatementConditionalClosing": 0,
|
||||
"IfStatementConditionalOpening": 0,
|
||||
"IfStatementOpeningBrace": 1,
|
||||
"LogicalExpressionOperator": 1,
|
||||
"MemberExpressionOpening": 0,
|
||||
"ObjectExpressionClosingBrace": 0,
|
||||
"ObjectExpressionOpeningBrace": 1,
|
||||
"ParameterComma": 1,
|
||||
"ParameterList": 0,
|
||||
"PropertyName": 0,
|
||||
"PropertyValue": 0,
|
||||
"SwitchDiscriminantClosing": 1,
|
||||
"SwitchDiscriminantOpening": 0,
|
||||
"ThrowKeyword": 1,
|
||||
"TryClosingBrace": 1,
|
||||
"TryKeyword": -1,
|
||||
"TryOpeningBrace": 1,
|
||||
"UnaryExpressionOperator": 0,
|
||||
"VariableName": 1,
|
||||
"WhileStatementClosingBrace": 1,
|
||||
"WhileStatementConditionalClosing": 1,
|
||||
"WhileStatementConditionalOpening": 0,
|
||||
"WhileStatementOpeningBrace": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
1
frontend/.eslintignore
Normal file
@@ -0,0 +1 @@
|
||||
**/JsLibraries/**
|
||||
327
frontend/.eslintrc.js
Normal file
@@ -0,0 +1,327 @@
|
||||
const fs = require('fs');
|
||||
|
||||
const dirs = fs
|
||||
.readdirSync('frontend/src', { withFileTypes: true })
|
||||
.filter((dirent) => dirent.isDirectory())
|
||||
.map((dirent) => dirent.name)
|
||||
.join('|');
|
||||
|
||||
module.exports = {
|
||||
parser: 'babel-eslint',
|
||||
|
||||
env: {
|
||||
browser: true,
|
||||
commonjs: true,
|
||||
node: true,
|
||||
es6: true
|
||||
},
|
||||
|
||||
globals: {
|
||||
expect: false,
|
||||
chai: false,
|
||||
sinon: false
|
||||
},
|
||||
|
||||
parserOptions: {
|
||||
ecmaVersion: 6,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
modules: true,
|
||||
impliedStrict: true
|
||||
}
|
||||
},
|
||||
|
||||
plugins: [
|
||||
'filenames',
|
||||
'react',
|
||||
'simple-import-sort',
|
||||
'import'
|
||||
],
|
||||
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect'
|
||||
}
|
||||
},
|
||||
|
||||
rules: {
|
||||
'filenames/match-exported': ['error'],
|
||||
|
||||
// ECMAScript 6
|
||||
|
||||
'arrow-body-style': [0],
|
||||
'arrow-parens': ['error', 'always'],
|
||||
'arrow-spacing': ['error', { before: true, after: true }],
|
||||
'constructor-super': 'error',
|
||||
'generator-star-spacing': 'off',
|
||||
'no-class-assign': 'error',
|
||||
'no-confusing-arrow': 'error',
|
||||
'no-const-assign': 'error',
|
||||
'no-dupe-class-members': 'error',
|
||||
'no-duplicate-imports': 'error',
|
||||
'no-new-symbol': 'error',
|
||||
'no-this-before-super': 'error',
|
||||
'no-useless-escape': 'error',
|
||||
'no-useless-computed-key': 'error',
|
||||
'no-useless-constructor': 'error',
|
||||
'no-var': 'warn',
|
||||
'object-shorthand': ['error', 'properties'],
|
||||
'prefer-arrow-callback': 'error',
|
||||
'prefer-const': 'warn',
|
||||
'prefer-reflect': 'off',
|
||||
'prefer-rest-params': 'off',
|
||||
'prefer-spread': 'warn',
|
||||
'prefer-template': 'error',
|
||||
'require-yield': 'off',
|
||||
'template-curly-spacing': ['error', 'never'],
|
||||
'yield-star-spacing': 'off',
|
||||
|
||||
// Possible Errors
|
||||
|
||||
'comma-dangle': 'error',
|
||||
'no-cond-assign': 'error',
|
||||
'no-console': 'off',
|
||||
'no-constant-condition': 'warn',
|
||||
'no-control-regex': 'error',
|
||||
'no-debugger': 'off',
|
||||
'no-dupe-args': 'error',
|
||||
'no-dupe-keys': 'error',
|
||||
'no-duplicate-case': 'error',
|
||||
'no-empty': 'warn',
|
||||
'no-empty-character-class': 'error',
|
||||
'no-ex-assign': 'error',
|
||||
'no-extra-boolean-cast': 'error',
|
||||
'no-extra-parens': ['error', 'functions'],
|
||||
'no-extra-semi': 'error',
|
||||
'no-func-assign': 'error',
|
||||
'no-inner-declarations': 'error',
|
||||
'no-invalid-regexp': 'error',
|
||||
'no-irregular-whitespace': 'error',
|
||||
'no-negated-in-lhs': 'error',
|
||||
'no-obj-calls': 'error',
|
||||
'no-regex-spaces': 'error',
|
||||
'no-sparse-arrays': 'error',
|
||||
'no-unexpected-multiline': 'error',
|
||||
'no-unreachable': 'warn',
|
||||
'no-unsafe-finally': 'error',
|
||||
'use-isnan': 'error',
|
||||
'valid-jsdoc': 'off',
|
||||
'valid-typeof': 'error',
|
||||
|
||||
// Best Practices
|
||||
|
||||
'accessor-pairs': 'off',
|
||||
'array-callback-return': 'warn',
|
||||
'block-scoped-var': 'warn',
|
||||
'consistent-return': 'off',
|
||||
curly: 'error',
|
||||
'default-case': 'error',
|
||||
'dot-location': ['error', 'property'],
|
||||
'dot-notation': 'error',
|
||||
eqeqeq: ['error', 'smart'],
|
||||
'guard-for-in': 'error',
|
||||
'no-alert': 'warn',
|
||||
'no-caller': 'error',
|
||||
'no-case-declarations': 'error',
|
||||
'no-div-regex': 'error',
|
||||
'no-else-return': 'error',
|
||||
'no-empty-function': ['error', { allow: ['arrowFunctions'] }],
|
||||
'no-empty-pattern': 'error',
|
||||
'no-eval': 'error',
|
||||
'no-extend-native': 'error',
|
||||
'no-extra-bind': 'error',
|
||||
'no-fallthrough': 'error',
|
||||
'no-floating-decimal': 'error',
|
||||
'no-implicit-coercion': ['error', {
|
||||
boolean: false,
|
||||
number: true,
|
||||
string: true,
|
||||
allow: [/* "!!", "~", "*", "+" */]
|
||||
}],
|
||||
'no-implicit-globals': 'error',
|
||||
'no-implied-eval': 'error',
|
||||
'no-invalid-this': 'off',
|
||||
'no-iterator': 'error',
|
||||
'no-labels': 'error',
|
||||
'no-lone-blocks': 'error',
|
||||
'no-loop-func': 'error',
|
||||
'no-magic-numbers': ['off', { ignoreArrayIndexes: true, ignore: [0, 1] }],
|
||||
'no-multi-spaces': 'error',
|
||||
'no-multi-str': 'error',
|
||||
'no-native-reassign': ['error', { exceptions: ['console'] }],
|
||||
'no-new': 'off',
|
||||
'no-new-func': 'error',
|
||||
'no-new-wrappers': 'error',
|
||||
'no-octal': 'error',
|
||||
'no-octal-escape': 'error',
|
||||
'no-param-reassign': 'off',
|
||||
'no-process-env': 'off',
|
||||
'no-proto': 'error',
|
||||
'no-redeclare': 'error',
|
||||
'no-return-assign': 'warn',
|
||||
'no-script-url': 'error',
|
||||
'no-self-assign': 'error',
|
||||
'no-self-compare': 'error',
|
||||
'no-sequences': 'error',
|
||||
'no-throw-literal': 'error',
|
||||
'no-unmodified-loop-condition': 'error',
|
||||
'no-unused-expressions': 'error',
|
||||
'no-unused-labels': 'error',
|
||||
'no-useless-call': 'error',
|
||||
'no-useless-concat': 'error',
|
||||
'no-void': 'error',
|
||||
'no-warning-comments': 'off',
|
||||
'no-with': 'error',
|
||||
radix: ['error', 'as-needed'],
|
||||
'vars-on-top': 'off',
|
||||
'wrap-iife': ['error', 'inside'],
|
||||
yoda: 'error',
|
||||
|
||||
// Strict Mode
|
||||
|
||||
strict: ['error', 'never'],
|
||||
|
||||
// Variables
|
||||
|
||||
'init-declarations': ['error', 'always'],
|
||||
'no-catch-shadow': 'error',
|
||||
'no-delete-var': 'error',
|
||||
'no-label-var': 'error',
|
||||
'no-restricted-globals': 'off',
|
||||
'no-shadow': 'error',
|
||||
'no-shadow-restricted-names': 'error',
|
||||
'no-undef': 'error',
|
||||
'no-undef-init': 'off',
|
||||
'no-undefined': 'off',
|
||||
'no-unused-vars': ['error', { args: 'none', ignoreRestSiblings: true }],
|
||||
'no-use-before-define': 'error',
|
||||
|
||||
// Node.js and CommonJS
|
||||
|
||||
'callback-return': 'warn',
|
||||
'global-require': 'error',
|
||||
'handle-callback-err': 'warn',
|
||||
'no-mixed-requires': 'error',
|
||||
'no-new-require': 'error',
|
||||
'no-path-concat': 'error',
|
||||
'no-process-exit': 'error',
|
||||
|
||||
// Stylistic Issues
|
||||
|
||||
'array-bracket-spacing': ['error', 'never'],
|
||||
'block-spacing': ['error', 'always'],
|
||||
'brace-style': ['error', '1tbs', { allowSingleLine: false }],
|
||||
camelcase: 'off',
|
||||
'comma-spacing': ['error', { before: false, after: true }],
|
||||
'comma-style': ['error', 'last'],
|
||||
'computed-property-spacing': ['error', 'never'],
|
||||
'consistent-this': ['error', 'self'],
|
||||
'eol-last': 'error',
|
||||
'func-names': 'off',
|
||||
'func-style': ['error', 'declaration'],
|
||||
indent: ['error', 2, { SwitchCase: 1 }],
|
||||
'key-spacing': ['error', { beforeColon: false, afterColon: true }],
|
||||
'keyword-spacing': ['error', { before: true, after: true }],
|
||||
'lines-around-comment': ['error', { beforeBlockComment: true, afterBlockComment: false }],
|
||||
'max-depth': ['error', { maximum: 5 }],
|
||||
'max-nested-callbacks': ['error', 4],
|
||||
'max-statements': 'off',
|
||||
'max-statements-per-line': ['error', { max: 1 }],
|
||||
'new-cap': ['error', { capIsNewExceptions: ['$.Deferred', 'DragDropContext', 'DragLayer', 'DragSource', 'DropTarget'] }],
|
||||
'new-parens': 'error',
|
||||
'newline-after-var': 'off',
|
||||
'newline-before-return': 'off',
|
||||
'newline-per-chained-call': 'off',
|
||||
'no-array-constructor': 'error',
|
||||
'no-bitwise': 'error',
|
||||
'no-continue': 'error',
|
||||
'no-inline-comments': 'off',
|
||||
'no-lonely-if': 'warn',
|
||||
'no-mixed-spaces-and-tabs': 'error',
|
||||
'no-multiple-empty-lines': ['error', { max: 1 }],
|
||||
'no-negated-condition': 'warn',
|
||||
'no-nested-ternary': 'error',
|
||||
'no-new-object': 'error',
|
||||
'no-plusplus': 'off',
|
||||
'no-restricted-syntax': 'off',
|
||||
'no-spaced-func': 'error',
|
||||
'no-ternary': 'off',
|
||||
'no-trailing-spaces': 'error',
|
||||
'no-underscore-dangle': ['error', { allowAfterThis: true }],
|
||||
'no-unneeded-ternary': 'error',
|
||||
'no-whitespace-before-property': 'error',
|
||||
'object-curly-spacing': ['error', 'always'],
|
||||
'one-var': ['error', 'never'],
|
||||
'one-var-declaration-per-line': ['error', 'always'],
|
||||
'operator-assignment': ['off', 'never'],
|
||||
'operator-linebreak': ['error', 'after'],
|
||||
'quote-props': ['error', 'as-needed'],
|
||||
quotes: ['error', 'single'],
|
||||
'require-jsdoc': 'off',
|
||||
semi: 'error',
|
||||
'semi-spacing': ['error', { before: false, after: true }],
|
||||
'sort-vars': 'off',
|
||||
'space-before-blocks': ['error', 'always'],
|
||||
'space-before-function-paren': ['error', 'never'],
|
||||
'space-in-parens': 'off',
|
||||
'space-infix-ops': 'off',
|
||||
'space-unary-ops': 'off',
|
||||
'spaced-comment': 'error',
|
||||
'wrap-regex': 'error',
|
||||
|
||||
// ImportSort
|
||||
|
||||
'simple-import-sort/sort': 'error',
|
||||
'import/newline-after-import': 'error',
|
||||
|
||||
// React
|
||||
|
||||
'react/jsx-boolean-value': [2, 'always'],
|
||||
'react/jsx-uses-vars': 2,
|
||||
'react/jsx-closing-bracket-location': 2,
|
||||
'react/jsx-tag-spacing': ['error'],
|
||||
'react/jsx-curly-spacing': [2, 'never'],
|
||||
'react/jsx-equals-spacing': [2, 'never'],
|
||||
'react/jsx-indent-props': [2, 2],
|
||||
'react/jsx-indent': [2, 2, { indentLogicalExpressions: true }],
|
||||
'react/jsx-key': 2,
|
||||
'react/jsx-no-bind': [2, { allowArrowFunctions: true }],
|
||||
'react/jsx-no-duplicate-props': [2, { ignoreCase: true }],
|
||||
'react/jsx-max-props-per-line': [2, { maximum: 2 }],
|
||||
'react/jsx-handler-names': [2, { eventHandlerPrefix: '(on|dispatch)', eventHandlerPropPrefix: 'on' }],
|
||||
'react/jsx-no-undef': 2,
|
||||
'react/jsx-pascal-case': 2,
|
||||
'react/jsx-uses-react': 2,
|
||||
// Explicitly disabled in case we want to enable them again
|
||||
'react/no-did-mount-set-state': 0,
|
||||
'react/no-did-update-set-state': 0,
|
||||
'react/no-direct-mutation-state': 2,
|
||||
'react/no-multi-comp': [2, { ignoreStateless: true }],
|
||||
'react/no-unknown-property': 2,
|
||||
'react/prefer-es6-class': 2,
|
||||
'react/prop-types': 2,
|
||||
'react/react-in-jsx-scope': 2,
|
||||
'react/self-closing-comp': 2,
|
||||
'react/sort-comp': 2,
|
||||
'react/jsx-wrap-multilines': 2
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.js'],
|
||||
rules: {
|
||||
'simple-import-sort/sort': [
|
||||
'error',
|
||||
{
|
||||
groups: [
|
||||
// Packages
|
||||
// Absolute Paths
|
||||
// Relative Paths
|
||||
// Css
|
||||
['^@?\\w', `^(${dirs})(/.*|$)`, '^\\.', '^\\..*css$']
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
12
frontend/.jsbeautifyrc
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"js": {
|
||||
"indent_size": 2,
|
||||
"indent_char": " ",
|
||||
"indent_level": 2,
|
||||
"indent_with_tabs": false,
|
||||
"preserve_newlines": true,
|
||||
"brace_style": "collapse",
|
||||
"max_preserve_newlines": 2,
|
||||
"jslint_happy": true
|
||||
}
|
||||
}
|
||||
396
frontend/.stylelintrc
Normal file
@@ -0,0 +1,396 @@
|
||||
{
|
||||
"plugins": [
|
||||
"stylelint-order"
|
||||
],
|
||||
"ignoreFiles": [
|
||||
"frontend/src/Styles/scaffolding.css",
|
||||
"**/*.js"
|
||||
],
|
||||
"rules": {
|
||||
"at-rule-empty-line-before": [
|
||||
"always",
|
||||
{
|
||||
"except": [
|
||||
"inside-block"
|
||||
]
|
||||
}
|
||||
],
|
||||
"at-rule-name-case": "lower",
|
||||
"at-rule-name-newline-after": "always-multi-line",
|
||||
"at-rule-name-space-after": "always",
|
||||
"at-rule-no-unknown": [
|
||||
true,
|
||||
{
|
||||
"ignoreAtRules": [
|
||||
"/^add\\-mixin$/",
|
||||
"/^define\\-mixin$/"
|
||||
]
|
||||
}
|
||||
],
|
||||
"at-rule-no-vendor-prefix": true,
|
||||
"at-rule-semicolon-newline-after": "always",
|
||||
"at-rule-semicolon-space-before": "never",
|
||||
"block-closing-brace-empty-line-before": "never",
|
||||
"block-closing-brace-newline-after": "always",
|
||||
"block-closing-brace-newline-before": "always",
|
||||
"block-closing-brace-space-after": "always-single-line",
|
||||
"block-closing-brace-space-before": "always-single-line",
|
||||
"block-no-empty": true,
|
||||
"block-opening-brace-newline-after": "always",
|
||||
"block-opening-brace-newline-before": "never-single-line",
|
||||
"block-opening-brace-space-after": "always-single-line",
|
||||
"block-opening-brace-space-before": "always",
|
||||
"color-hex-case": "lower",
|
||||
"color-hex-length": "short",
|
||||
"color-named": "never",
|
||||
"color-no-invalid-hex": true,
|
||||
"comment-whitespace-inside": "always",
|
||||
"declaration-bang-space-after": "never",
|
||||
"declaration-bang-space-before": "always",
|
||||
"declaration-block-no-duplicate-properties": [
|
||||
true,
|
||||
{
|
||||
"ignoreProperties": [
|
||||
"composes"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declaration-block-no-redundant-longhand-properties": true,
|
||||
"declaration-block-no-shorthand-property-overrides": true,
|
||||
"declaration-block-semicolon-newline-after": "always",
|
||||
"declaration-block-semicolon-newline-before": "never-multi-line",
|
||||
"declaration-block-semicolon-space-before": "never",
|
||||
"declaration-block-single-line-max-declarations": 1,
|
||||
"declaration-block-trailing-semicolon": "always",
|
||||
"declaration-colon-space-after": "always",
|
||||
"declaration-colon-space-before": "never",
|
||||
"font-family-name-quotes": "always-unless-keyword",
|
||||
"function-calc-no-unspaced-operator": true,
|
||||
"function-comma-newline-after": "never-multi-line",
|
||||
"function-comma-newline-before": "never-multi-line",
|
||||
"function-comma-space-after": "always",
|
||||
"function-comma-space-before": "never",
|
||||
"function-linear-gradient-no-nonstandard-direction": true,
|
||||
"function-name-case": "lower",
|
||||
"function-parentheses-newline-inside": "never-multi-line",
|
||||
"function-parentheses-space-inside": "never",
|
||||
"function-url-quotes": "always",
|
||||
"function-url-scheme-disallowed-list": [
|
||||
"data"
|
||||
],
|
||||
"function-whitespace-after": "always",
|
||||
"indentation": 2,
|
||||
"keyframe-declaration-no-important": true,
|
||||
"length-zero-no-unit": true,
|
||||
"max-empty-lines": 1,
|
||||
"max-line-length": [
|
||||
100,
|
||||
{
|
||||
"ignore": [
|
||||
"non-comments"
|
||||
]
|
||||
}
|
||||
],
|
||||
"max-nesting-depth": 2,
|
||||
"media-feature-colon-space-after": "always",
|
||||
"media-feature-colon-space-before": "never",
|
||||
"media-feature-name-case": "lower",
|
||||
"media-feature-name-no-vendor-prefix": true,
|
||||
"media-feature-range-operator-space-after": "always",
|
||||
"media-feature-range-operator-space-before": "always",
|
||||
"no-empty-source": true,
|
||||
"no-eol-whitespace": true,
|
||||
"no-extra-semicolons": true,
|
||||
"no-invalid-double-slash-comments": true,
|
||||
"no-missing-end-of-source-newline": true,
|
||||
"number-leading-zero": "always",
|
||||
"number-no-trailing-zeros": true,
|
||||
"order/order": [
|
||||
"custom-properties",
|
||||
"dollar-variables",
|
||||
{
|
||||
"hasBlock": false,
|
||||
"name": "add-mixin",
|
||||
"type": "at-rule"
|
||||
},
|
||||
"declarations",
|
||||
"rules",
|
||||
"at-rules"
|
||||
],
|
||||
"order/properties-order": [
|
||||
{
|
||||
"emptyLineBefore": "always",
|
||||
"properties": [
|
||||
"composes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"emptyLineBefore": "always",
|
||||
"properties": [
|
||||
"position",
|
||||
"top",
|
||||
"right",
|
||||
"bottom",
|
||||
"left",
|
||||
"z-index",
|
||||
"display",
|
||||
"visibility",
|
||||
"align-content",
|
||||
"align-items",
|
||||
"align-self",
|
||||
"justify-content",
|
||||
"flex",
|
||||
"flex-direction",
|
||||
"flex-order",
|
||||
"flex-pack",
|
||||
"flex-align",
|
||||
"flex-grow",
|
||||
"flex-shrink",
|
||||
"flex-basis",
|
||||
"flex-wrap",
|
||||
"flex-flow",
|
||||
"float",
|
||||
"clear",
|
||||
"overflow",
|
||||
"overflow-x",
|
||||
"overflow-y",
|
||||
"-webkit-overflow-scrolling",
|
||||
"clip",
|
||||
"box-sizing",
|
||||
"margin",
|
||||
"margin-top",
|
||||
"margin-right",
|
||||
"margin-bottom",
|
||||
"margin-left",
|
||||
"padding",
|
||||
"padding-top",
|
||||
"padding-right",
|
||||
"padding-bottom",
|
||||
"padding-left",
|
||||
"min-width",
|
||||
"min-height",
|
||||
"max-width",
|
||||
"max-height",
|
||||
"width",
|
||||
"height",
|
||||
"outline",
|
||||
"outline-width",
|
||||
"outline-style",
|
||||
"outline-color",
|
||||
"outline-offset",
|
||||
"border",
|
||||
"border-spacing",
|
||||
"border-collapse",
|
||||
"border-width",
|
||||
"border-style",
|
||||
"border-color",
|
||||
"border-top",
|
||||
"border-top-width",
|
||||
"border-top-style",
|
||||
"border-top-color",
|
||||
"border-right",
|
||||
"border-right-width",
|
||||
"border-right-style",
|
||||
"border-right-color",
|
||||
"border-bottom",
|
||||
"border-bottom-width",
|
||||
"border-bottom-style",
|
||||
"border-bottom-color",
|
||||
"border-left",
|
||||
"border-left-width",
|
||||
"border-left-style",
|
||||
"border-left-color",
|
||||
"border-radius",
|
||||
"border-top-left-radius",
|
||||
"border-top-right-radius",
|
||||
"border-bottom-right-radius",
|
||||
"border-bottom-left-radius",
|
||||
"border-image",
|
||||
"border-image-source",
|
||||
"border-image-slice",
|
||||
"border-image-width",
|
||||
"border-image-outset",
|
||||
"border-image-repeat",
|
||||
"border-top-image",
|
||||
"border-right-image",
|
||||
"border-bottom-image",
|
||||
"border-left-image",
|
||||
"border-corner-image",
|
||||
"border-top-left-image",
|
||||
"border-top-right-image",
|
||||
"border-bottom-right-image",
|
||||
"border-bottom-left-image",
|
||||
"background",
|
||||
"background-color",
|
||||
"background-image",
|
||||
"background-attachment",
|
||||
"background-position",
|
||||
"background-position-x",
|
||||
"background-position-y",
|
||||
"background-clip",
|
||||
"background-origin",
|
||||
"background-size",
|
||||
"background-repeat",
|
||||
"box-decoration-break",
|
||||
"box-shadow",
|
||||
"color",
|
||||
"table-layout",
|
||||
"caption-side",
|
||||
"empty-cells",
|
||||
"list-style",
|
||||
"list-style-position",
|
||||
"list-style-type",
|
||||
"list-style-image",
|
||||
"quotes",
|
||||
"content",
|
||||
"counter-increment",
|
||||
"counter-reset",
|
||||
"-ms-writing-mode",
|
||||
"vertical-align",
|
||||
"text-align",
|
||||
"text-align-last",
|
||||
"text-decoration",
|
||||
"text-emphasis",
|
||||
"text-emphasis-position",
|
||||
"text-emphasis-style",
|
||||
"text-emphasis-color",
|
||||
"text-indent",
|
||||
"text-justify",
|
||||
"text-outline",
|
||||
"text-transform",
|
||||
"text-wrap",
|
||||
"text-overflow",
|
||||
"text-overflow-ellipsis",
|
||||
"text-overflow-mode",
|
||||
"text-shadow",
|
||||
"white-space",
|
||||
"word-spacing",
|
||||
"word-wrap",
|
||||
"word-break",
|
||||
"tab-size",
|
||||
"hyphens",
|
||||
"letter-spacing",
|
||||
"font",
|
||||
"font-weight",
|
||||
"font-style",
|
||||
"font-variant",
|
||||
"font-size-adjust",
|
||||
"font-stretch",
|
||||
"font-size",
|
||||
"font-family",
|
||||
"font-smoothing",
|
||||
"-moz-osx-font-smoothing",
|
||||
"-webkit-font-smoothing",
|
||||
"src",
|
||||
"line-height",
|
||||
"opacity",
|
||||
"filter",
|
||||
"resize",
|
||||
"cursor",
|
||||
"appearance",
|
||||
"nav-index",
|
||||
"nav-up",
|
||||
"nav-right",
|
||||
"nav-down",
|
||||
"nav-left",
|
||||
"transition",
|
||||
"transition-delay",
|
||||
"transition-timing-function",
|
||||
"transition-duration",
|
||||
"transition-property",
|
||||
"transform",
|
||||
"transform-origin",
|
||||
"transform-style",
|
||||
"backface-visibility",
|
||||
"animation",
|
||||
"animation-name",
|
||||
"animation-duration",
|
||||
"animation-play-state",
|
||||
"animation-timing-function",
|
||||
"animation-delay",
|
||||
"animation-iteration-count",
|
||||
"animation-direction",
|
||||
"animation-fill-mode",
|
||||
"pointer-events",
|
||||
"user-select",
|
||||
"touch-action",
|
||||
"-webkit-tap-highlight-color",
|
||||
"unicode-bidi",
|
||||
"direction",
|
||||
"columns",
|
||||
"column-span",
|
||||
"column-width",
|
||||
"column-count",
|
||||
"column-fill",
|
||||
"column-gap",
|
||||
"column-rule",
|
||||
"column-rule-width",
|
||||
"column-rule-style",
|
||||
"column-rule-color",
|
||||
"break-before",
|
||||
"break-inside",
|
||||
"break-after",
|
||||
"page-break-before",
|
||||
"page-break-inside",
|
||||
"page-break-after",
|
||||
"orphans",
|
||||
"widows",
|
||||
"zoom",
|
||||
"max-zoom",
|
||||
"min-zoom",
|
||||
"user-zoom",
|
||||
"orientation"
|
||||
]
|
||||
}
|
||||
],
|
||||
"property-case": "lower",
|
||||
"property-no-vendor-prefix": true,
|
||||
"rule-empty-line-before": [
|
||||
"always",
|
||||
{
|
||||
"except": [
|
||||
"first-nested"
|
||||
],
|
||||
"ignore": [
|
||||
"after-comment"
|
||||
]
|
||||
}
|
||||
],
|
||||
"selector-attribute-brackets-space-inside": "never",
|
||||
"selector-attribute-operator-space-after": "never",
|
||||
"selector-attribute-operator-space-before": "never",
|
||||
"selector-attribute-quotes": "never",
|
||||
"selector-class-pattern": "^[A-Za-z0-9]+$",
|
||||
"selector-combinator-space-after": "always",
|
||||
"selector-combinator-space-before": "always",
|
||||
"selector-descendant-combinator-no-non-space": true,
|
||||
"selector-list-comma-newline-after": "always",
|
||||
"selector-list-comma-newline-before": "never-multi-line",
|
||||
"selector-list-comma-space-before": "never",
|
||||
"selector-max-attribute": 0,
|
||||
"selector-max-class": 3,
|
||||
"selector-max-compound-selectors": 3,
|
||||
"selector-max-empty-lines": 0,
|
||||
"selector-max-id": 0,
|
||||
"selector-max-universal": 0,
|
||||
"selector-pseudo-class-case": "lower",
|
||||
"selector-pseudo-class-parentheses-space-inside": "never",
|
||||
"selector-pseudo-element-case": "lower",
|
||||
"selector-pseudo-element-colon-notation": "double",
|
||||
"selector-pseudo-element-no-unknown": true,
|
||||
"selector-type-case": "lower",
|
||||
"selector-type-no-unknown": true,
|
||||
"shorthand-property-no-redundant-values": true,
|
||||
"string-no-newline": true,
|
||||
"string-quotes": "single",
|
||||
"time-min-milliseconds": 100,
|
||||
"unit-case": "lower",
|
||||
"unit-no-unknown": true,
|
||||
"value-list-comma-newline-after": "never-multi-line",
|
||||
"value-list-comma-newline-before": "never-multi-line",
|
||||
"value-list-comma-space-after": "always",
|
||||
"value-list-comma-space-before": "never",
|
||||
"value-list-max-empty-lines": 0,
|
||||
"value-no-vendor-prefix": true
|
||||
}
|
||||
}
|
||||
7
frontend/.tern-project
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ecmaVersion": 6,
|
||||
"libs": [
|
||||
"browser",
|
||||
"jquery"
|
||||
]
|
||||
}
|
||||
35
frontend/babel.config.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const loose = true;
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
// Stage 1
|
||||
'@babel/plugin-proposal-export-default-from',
|
||||
['@babel/plugin-proposal-optional-chaining', { loose }],
|
||||
['@babel/plugin-proposal-nullish-coalescing-operator', { loose }],
|
||||
|
||||
// Stage 2
|
||||
'@babel/plugin-proposal-export-namespace-from',
|
||||
|
||||
// Stage 3
|
||||
['@babel/plugin-proposal-class-properties', { loose }],
|
||||
'@babel/plugin-syntax-dynamic-import'
|
||||
],
|
||||
env: {
|
||||
development: {
|
||||
presets: [
|
||||
['@babel/preset-react', { development: true }]
|
||||
],
|
||||
plugins: [
|
||||
'babel-plugin-inline-classnames'
|
||||
]
|
||||
},
|
||||
production: {
|
||||
presets: [
|
||||
'@babel/preset-react'
|
||||
],
|
||||
plugins: [
|
||||
'babel-plugin-transform-react-remove-prop-types'
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
17
frontend/gulp/build.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const gulp = require('gulp');
|
||||
|
||||
require('./clean');
|
||||
require('./copy');
|
||||
require('./webpack');
|
||||
|
||||
gulp.task('build',
|
||||
gulp.series('clean',
|
||||
gulp.parallel(
|
||||
'webpack',
|
||||
'copyHtml',
|
||||
'copyFonts',
|
||||
'copyImages'
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
8
frontend/gulp/clean.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const gulp = require('gulp');
|
||||
const del = require('del');
|
||||
|
||||
const paths = require('./helpers/paths');
|
||||
|
||||
gulp.task('clean', () => {
|
||||
return del([paths.dest.root]);
|
||||
});
|
||||
34
frontend/gulp/copy.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const path = require('path');
|
||||
const gulp = require('gulp');
|
||||
const print = require('gulp-print').default;
|
||||
const cache = require('gulp-cached');
|
||||
const livereload = require('gulp-livereload');
|
||||
const paths = require('./helpers/paths.js');
|
||||
|
||||
gulp.task('copyHtml', () => {
|
||||
return gulp.src(paths.src.html, { base: paths.src.root })
|
||||
.pipe(cache('copyHtml'))
|
||||
.pipe(print())
|
||||
.pipe(gulp.dest(paths.dest.root))
|
||||
.pipe(livereload());
|
||||
});
|
||||
|
||||
gulp.task('copyFonts', () => {
|
||||
return gulp.src(
|
||||
path.join(paths.src.fonts, '**', '*.*'), { base: paths.src.root }
|
||||
)
|
||||
.pipe(cache('copyFonts'))
|
||||
.pipe(print())
|
||||
.pipe(gulp.dest(paths.dest.root))
|
||||
.pipe(livereload());
|
||||
});
|
||||
|
||||
gulp.task('copyImages', () => {
|
||||
return gulp.src(
|
||||
path.join(paths.src.images, '**', '*.*'), { base: paths.src.root }
|
||||
)
|
||||
.pipe(cache('copyImages'))
|
||||
.pipe(print())
|
||||
.pipe(gulp.dest(paths.dest.root))
|
||||
.pipe(livereload());
|
||||
});
|
||||
5
frontend/gulp/gulpFile.js
Normal file
@@ -0,0 +1,5 @@
|
||||
require('./build.js');
|
||||
require('./clean.js');
|
||||
require('./copy.js');
|
||||
require('./watch.js');
|
||||
require('./webpack.js');
|
||||
6
frontend/gulp/helpers/errorHandler.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const colors = require('ansi-colors');
|
||||
|
||||
module.exports = function errorHandler(error) {
|
||||
console.log(colors.red(`Error (${error.plugin}): ${error.message}`));
|
||||
this.emit('end');
|
||||
};
|
||||
23
frontend/gulp/helpers/paths.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const root = './frontend/src';
|
||||
|
||||
const paths = {
|
||||
src: {
|
||||
root,
|
||||
html: `${root}/*.html`,
|
||||
scripts: `${root}/**/*.js`,
|
||||
content: `${root}/Content/`,
|
||||
fonts: `${root}/Content/Fonts/`,
|
||||
images: `${root}/Content/Images/`,
|
||||
exclude: {
|
||||
libs: `!${root}/JsLibraries/**`
|
||||
}
|
||||
},
|
||||
dest: {
|
||||
root: './_output/UI/',
|
||||
content: './_output/UI/Content/',
|
||||
fonts: './_output/UI/Content/Fonts/',
|
||||
images: './_output/UI/Content/Images/'
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = paths;
|
||||
18
frontend/gulp/watch.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const gulp = require('gulp');
|
||||
const livereload = require('gulp-livereload');
|
||||
const gulpWatch = require('gulp-watch');
|
||||
const paths = require('./helpers/paths.js');
|
||||
|
||||
require('./copy.js');
|
||||
require('./webpack.js');
|
||||
|
||||
function watch() {
|
||||
livereload.listen({ start: true });
|
||||
|
||||
gulp.task('webpackWatch')();
|
||||
gulpWatch(paths.src.html, gulp.series('copyHtml'));
|
||||
gulpWatch(`${paths.src.fonts}**/*.*`, gulp.series('copyFonts'));
|
||||
gulpWatch(`${paths.src.images}**/*.*`, gulp.series('copyImages'));
|
||||
}
|
||||
|
||||
gulp.task('watch', gulp.series('build', watch));
|
||||
271
frontend/gulp/webpack.js
Normal file
@@ -0,0 +1,271 @@
|
||||
const gulp = require('gulp');
|
||||
const webpackStream = require('webpack-stream');
|
||||
const livereload = require('gulp-livereload');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const errorHandler = require('./helpers/errorHandler');
|
||||
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const HtmlWebpackPluginHtmlTags = require('html-webpack-plugin/lib/html-tags');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
|
||||
const uiFolder = 'UI';
|
||||
const frontendFolder = path.join(__dirname, '..');
|
||||
const srcFolder = path.join(frontendFolder, 'src');
|
||||
const isProduction = process.argv.indexOf('--production') > -1;
|
||||
const isProfiling = isProduction && process.argv.indexOf('--profile') > -1;
|
||||
const inlineWebWorkers = 'no-fallback';
|
||||
|
||||
const distFolder = path.resolve(frontendFolder, '..', '_output', uiFolder);
|
||||
|
||||
console.log('Source Folder:', srcFolder);
|
||||
console.log('Output Folder:', distFolder);
|
||||
console.log('isProduction:', isProduction);
|
||||
console.log('isProfiling:', isProfiling);
|
||||
|
||||
const cssVarsFiles = [
|
||||
'../src/Styles/Variables/colors',
|
||||
'../src/Styles/Variables/dimensions',
|
||||
'../src/Styles/Variables/fonts',
|
||||
'../src/Styles/Variables/animations',
|
||||
'../src/Styles/Variables/zIndexes'
|
||||
].map(require.resolve);
|
||||
|
||||
// Override the way HtmlWebpackPlugin injects the scripts
|
||||
// TODO: Find a better way to get these paths without
|
||||
HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function(html, assets, assetTags) {
|
||||
const head = assetTags.headTags.map((v) => {
|
||||
const href = v.attributes.href
|
||||
.replace('\\', '/')
|
||||
.replace('%5C', '/');
|
||||
|
||||
v.attributes = { rel: 'stylesheet', type: 'text/css', href: `/${href}` };
|
||||
return HtmlWebpackPluginHtmlTags.htmlTagObjectToString(v, this.options.xhtml);
|
||||
});
|
||||
const body = assetTags.bodyTags.map((v) => {
|
||||
v.attributes = { src: `/${v.attributes.src}` };
|
||||
return HtmlWebpackPluginHtmlTags.htmlTagObjectToString(v, this.options.xhtml);
|
||||
});
|
||||
|
||||
return html
|
||||
.replace('<!-- webpack bundles head -->', head.join('\r\n '))
|
||||
.replace('<!-- webpack bundles body -->', body.join('\r\n '));
|
||||
};
|
||||
|
||||
const plugins = [
|
||||
new OptimizeCssAssetsPlugin({}),
|
||||
|
||||
new webpack.DefinePlugin({
|
||||
__DEV__: !isProduction,
|
||||
'process.env.NODE_ENV': isProduction ? JSON.stringify('production') : JSON.stringify('development')
|
||||
}),
|
||||
|
||||
new MiniCssExtractPlugin({
|
||||
filename: path.join('Content', 'styles.css')
|
||||
}),
|
||||
|
||||
new HtmlWebpackPlugin({
|
||||
template: 'frontend/src/index.html',
|
||||
filename: 'index.html'
|
||||
})
|
||||
];
|
||||
|
||||
const config = {
|
||||
mode: isProduction ? 'production' : 'development',
|
||||
devtool: '#source-map',
|
||||
|
||||
stats: {
|
||||
children: false
|
||||
},
|
||||
|
||||
watchOptions: {
|
||||
ignored: /node_modules/
|
||||
},
|
||||
|
||||
entry: {
|
||||
index: 'index.js'
|
||||
},
|
||||
|
||||
resolve: {
|
||||
modules: [
|
||||
srcFolder,
|
||||
path.join(srcFolder, 'Shims'),
|
||||
'node_modules'
|
||||
],
|
||||
alias: {
|
||||
jquery: 'jquery/src/jquery'
|
||||
}
|
||||
},
|
||||
|
||||
output: {
|
||||
path: distFolder,
|
||||
filename: '[name].js',
|
||||
sourceMapFilename: '[file].map'
|
||||
},
|
||||
|
||||
optimization: {
|
||||
chunkIds: 'named',
|
||||
splitChunks: {
|
||||
chunks: 'initial'
|
||||
}
|
||||
},
|
||||
|
||||
performance: {
|
||||
hints: false
|
||||
},
|
||||
|
||||
plugins,
|
||||
|
||||
resolveLoader: {
|
||||
modules: [
|
||||
'node_modules',
|
||||
'frontend/gulp/webpack/'
|
||||
]
|
||||
},
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.worker\.js$/,
|
||||
use: {
|
||||
loader: 'worker-loader',
|
||||
options: {
|
||||
filename: '[name].js',
|
||||
inline: inlineWebWorkers
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.js?$/,
|
||||
exclude: /(node_modules|JsLibraries)/,
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
configFile: `${frontendFolder}/babel.config.js`,
|
||||
envName: isProduction ? 'production' : 'development',
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
modules: false,
|
||||
loose: true,
|
||||
debug: false,
|
||||
useBuiltIns: 'entry',
|
||||
corejs: 3
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// CSS Modules
|
||||
{
|
||||
test: /\.css$/,
|
||||
exclude: /(node_modules|globals.css)/,
|
||||
use: [
|
||||
{ loader: MiniCssExtractPlugin.loader },
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
modules: {
|
||||
localIdentName: '[name]/[local]/[hash:base64:5]'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
ident: 'postcss',
|
||||
config: {
|
||||
ctx: {
|
||||
cssVarsFiles
|
||||
},
|
||||
path: 'frontend/postcss.config.js'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// Global styles
|
||||
{
|
||||
test: /\.css$/,
|
||||
include: /(node_modules|globals.css)/,
|
||||
use: [
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// Fonts
|
||||
{
|
||||
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10240,
|
||||
mimetype: 'application/font-woff',
|
||||
emitFile: false,
|
||||
name: 'Content/Fonts/[name].[ext]'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
test: /\.(ttf|eot|eot?#iefix|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
emitFile: false,
|
||||
name: 'Content/Fonts/[name].[ext]'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
if (isProfiling) {
|
||||
config.resolve.alias['react-dom$'] = 'react-dom/profiling';
|
||||
config.resolve.alias['scheduler/tracing'] = 'scheduler/tracing-profiling';
|
||||
|
||||
config.optimization.minimizer = [
|
||||
new TerserPlugin({
|
||||
cache: true,
|
||||
parallel: true,
|
||||
sourceMap: true, // Must be set to true if using source-maps in production
|
||||
terserOptions: {
|
||||
mangle: false,
|
||||
keep_classnames: true,
|
||||
keep_fnames: true
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
gulp.task('webpack', () => {
|
||||
return webpackStream(config)
|
||||
.pipe(gulp.dest('_output/UI'));
|
||||
});
|
||||
|
||||
gulp.task('webpackWatch', () => {
|
||||
config.watch = true;
|
||||
|
||||
return webpackStream(config, webpack)
|
||||
.on('error', errorHandler)
|
||||
.pipe(gulp.dest('_output/UI'))
|
||||
.on('error', errorHandler)
|
||||
.pipe(livereload())
|
||||
.on('error', errorHandler);
|
||||
});
|
||||
11
frontend/gulp/webpack/css-variables-loader.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const loaderUtils = require('loader-utils');
|
||||
|
||||
module.exports = function cssVariablesLoader(source) {
|
||||
const options = loaderUtils.getOptions(this);
|
||||
|
||||
options.cssVarsFiles.forEach((cssVarsFile) => {
|
||||
this.addDependency(cssVarsFile);
|
||||
});
|
||||
|
||||
return source;
|
||||
};
|
||||
20
frontend/jsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"checkJs": false,
|
||||
"baseUrl": "src",
|
||||
"jsx": "react",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"paths": {
|
||||
"*": [
|
||||
"*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
]
|
||||
}
|
||||
23
frontend/postcss.config.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const reload = require('require-nocache')(module);
|
||||
|
||||
module.exports = (ctx, configPath, options) => {
|
||||
const config = {
|
||||
plugins: {
|
||||
'postcss-mixins': {
|
||||
mixinsDir: [
|
||||
'frontend/src/Styles/Mixins'
|
||||
]
|
||||
},
|
||||
'postcss-simple-vars': {
|
||||
variables: () =>
|
||||
ctx.options.cssVarsFiles.reduce((acc, vars) => {
|
||||
return Object.assign(acc, reload(vars));
|
||||
}, {})
|
||||
},
|
||||
'postcss-color-function': {},
|
||||
'postcss-nested': {}
|
||||
}
|
||||
};
|
||||
|
||||
return config;
|
||||
};
|
||||
4
frontend/src/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.insertFinalNewline": true
|
||||
}
|
||||
235
frontend/src/Activity/Blacklist/Blacklist.js
Normal file
@@ -0,0 +1,235 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
|
||||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||
import TablePager from 'Components/Table/TablePager';
|
||||
import { align, icons, kinds } from 'Helpers/Props';
|
||||
import getRemovedItems from 'Utilities/Object/getRemovedItems';
|
||||
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
||||
import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState';
|
||||
import selectAll from 'Utilities/Table/selectAll';
|
||||
import toggleSelected from 'Utilities/Table/toggleSelected';
|
||||
import BlacklistRowConnector from './BlacklistRowConnector';
|
||||
|
||||
class Blacklist extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
allSelected: false,
|
||||
allUnselected: false,
|
||||
lastToggled: null,
|
||||
selectedState: {},
|
||||
isConfirmRemoveModalOpen: false,
|
||||
items: props.items
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
items
|
||||
} = this.props;
|
||||
|
||||
if (hasDifferentItems(prevProps.items, items)) {
|
||||
this.setState((state) => {
|
||||
return {
|
||||
...removeOldSelectedState(state, getRemovedItems(prevProps.items, items)),
|
||||
items
|
||||
};
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
getSelectedIds = () => {
|
||||
return getSelectedIds(this.state.selectedState);
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onSelectAllChange = ({ value }) => {
|
||||
this.setState(selectAll(this.state.selectedState, value));
|
||||
}
|
||||
|
||||
onSelectedChange = ({ id, value, shiftKey = false }) => {
|
||||
this.setState((state) => {
|
||||
return toggleSelected(state, this.props.items, id, value, shiftKey);
|
||||
});
|
||||
}
|
||||
|
||||
onRemoveSelectedPress = () => {
|
||||
this.setState({ isConfirmRemoveModalOpen: true });
|
||||
}
|
||||
|
||||
onRemoveSelectedConfirmed = () => {
|
||||
this.props.onRemoveSelected(this.getSelectedIds());
|
||||
this.setState({ isConfirmRemoveModalOpen: false });
|
||||
}
|
||||
|
||||
onConfirmRemoveModalClose = () => {
|
||||
this.setState({ isConfirmRemoveModalOpen: false });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
error,
|
||||
items,
|
||||
columns,
|
||||
totalRecords,
|
||||
isRemoving,
|
||||
isClearingBlacklistExecuting,
|
||||
onClearBlacklistPress,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
allSelected,
|
||||
allUnselected,
|
||||
selectedState,
|
||||
isConfirmRemoveModalOpen
|
||||
} = this.state;
|
||||
|
||||
const selectedIds = this.getSelectedIds();
|
||||
|
||||
return (
|
||||
<PageContent title={translate('Blacklist')}>
|
||||
<PageToolbar>
|
||||
<PageToolbarSection>
|
||||
<PageToolbarButton
|
||||
label="Remove Selected"
|
||||
iconName={icons.REMOVE}
|
||||
isDisabled={!selectedIds.length}
|
||||
isSpinning={isRemoving}
|
||||
onPress={this.onRemoveSelectedPress}
|
||||
/>
|
||||
|
||||
<PageToolbarButton
|
||||
label={translate('Clear')}
|
||||
iconName={icons.CLEAR}
|
||||
isSpinning={isClearingBlacklistExecuting}
|
||||
onPress={onClearBlacklistPress}
|
||||
/>
|
||||
</PageToolbarSection>
|
||||
|
||||
<PageToolbarSection alignContent={align.RIGHT}>
|
||||
<TableOptionsModalWrapper
|
||||
{...otherProps}
|
||||
columns={columns}
|
||||
>
|
||||
<PageToolbarButton
|
||||
label={translate('Options')}
|
||||
iconName={icons.TABLE}
|
||||
/>
|
||||
</TableOptionsModalWrapper>
|
||||
</PageToolbarSection>
|
||||
</PageToolbar>
|
||||
|
||||
<PageContentBody>
|
||||
{
|
||||
isFetching && !isPopulated &&
|
||||
<LoadingIndicator />
|
||||
}
|
||||
|
||||
{
|
||||
!isFetching && !!error &&
|
||||
<div>
|
||||
{translate('UnableToLoadBlacklist')}
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
isPopulated && !error && !items.length &&
|
||||
<div>
|
||||
{translate('NoHistory')}
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
isPopulated && !error && !!items.length &&
|
||||
<div>
|
||||
<Table
|
||||
selectAll={true}
|
||||
allSelected={allSelected}
|
||||
allUnselected={allUnselected}
|
||||
columns={columns}
|
||||
{...otherProps}
|
||||
onSelectAllChange={this.onSelectAllChange}
|
||||
>
|
||||
<TableBody>
|
||||
{
|
||||
items.map((item) => {
|
||||
return (
|
||||
<BlacklistRowConnector
|
||||
key={item.id}
|
||||
isSelected={selectedState[item.id] || false}
|
||||
columns={columns}
|
||||
{...item}
|
||||
onSelectedChange={this.onSelectedChange}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<TablePager
|
||||
totalRecords={totalRecords}
|
||||
isFetching={isFetching}
|
||||
{...otherProps}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</PageContentBody>
|
||||
|
||||
<ConfirmModal
|
||||
isOpen={isConfirmRemoveModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title="Remove Selected"
|
||||
message={'Are you sure you want to remove the selected items from the blacklist?'}
|
||||
confirmLabel="Remove Selected"
|
||||
onConfirm={this.onRemoveSelectedConfirmed}
|
||||
onCancel={this.onConfirmRemoveModalClose}
|
||||
/>
|
||||
</PageContent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Blacklist.propTypes = {
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
totalRecords: PropTypes.number,
|
||||
isRemoving: PropTypes.bool.isRequired,
|
||||
isClearingBlacklistExecuting: PropTypes.bool.isRequired,
|
||||
onRemoveSelected: PropTypes.func.isRequired,
|
||||
onClearBlacklistPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default Blacklist;
|
||||
160
frontend/src/Activity/Blacklist/BlacklistConnector.js
Normal file
@@ -0,0 +1,160 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import withCurrentPage from 'Components/withCurrentPage';
|
||||
import * as blacklistActions from 'Store/Actions/blacklistActions';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
|
||||
import Blacklist from './Blacklist';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.blacklist,
|
||||
createCommandExecutingSelector(commandNames.CLEAR_BLACKLIST),
|
||||
(blacklist, isClearingBlacklistExecuting) => {
|
||||
return {
|
||||
isClearingBlacklistExecuting,
|
||||
...blacklist
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
...blacklistActions,
|
||||
executeCommand
|
||||
};
|
||||
|
||||
class BlacklistConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
const {
|
||||
useCurrentPage,
|
||||
fetchBlacklist,
|
||||
gotoBlacklistFirstPage
|
||||
} = this.props;
|
||||
|
||||
registerPagePopulator(this.repopulate);
|
||||
|
||||
if (useCurrentPage) {
|
||||
fetchBlacklist();
|
||||
} else {
|
||||
gotoBlacklistFirstPage();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.isClearingBlacklistExecuting && !this.props.isClearingBlacklistExecuting) {
|
||||
this.props.gotoBlacklistFirstPage();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.clearBlacklist();
|
||||
unregisterPagePopulator(this.repopulate);
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
repopulate = () => {
|
||||
this.props.fetchBlacklist();
|
||||
}
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onFirstPagePress = () => {
|
||||
this.props.gotoBlacklistFirstPage();
|
||||
}
|
||||
|
||||
onPreviousPagePress = () => {
|
||||
this.props.gotoBlacklistPreviousPage();
|
||||
}
|
||||
|
||||
onNextPagePress = () => {
|
||||
this.props.gotoBlacklistNextPage();
|
||||
}
|
||||
|
||||
onLastPagePress = () => {
|
||||
this.props.gotoBlacklistLastPage();
|
||||
}
|
||||
|
||||
onPageSelect = (page) => {
|
||||
this.props.gotoBlacklistPage({ page });
|
||||
}
|
||||
|
||||
onRemoveSelected = (ids) => {
|
||||
this.props.removeBlacklistItems({ ids });
|
||||
}
|
||||
|
||||
onSortPress = (sortKey) => {
|
||||
this.props.setBlacklistSort({ sortKey });
|
||||
}
|
||||
|
||||
onTableOptionChange = (payload) => {
|
||||
this.props.setBlacklistTableOption(payload);
|
||||
|
||||
if (payload.pageSize) {
|
||||
this.props.gotoBlacklistFirstPage();
|
||||
}
|
||||
}
|
||||
|
||||
onClearBlacklistPress = () => {
|
||||
this.props.executeCommand({ name: commandNames.CLEAR_BLACKLIST });
|
||||
}
|
||||
|
||||
onTableOptionChange = (payload) => {
|
||||
this.props.setBlacklistTableOption(payload);
|
||||
|
||||
if (payload.pageSize) {
|
||||
this.props.gotoBlacklistFirstPage();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Blacklist
|
||||
onFirstPagePress={this.onFirstPagePress}
|
||||
onPreviousPagePress={this.onPreviousPagePress}
|
||||
onNextPagePress={this.onNextPagePress}
|
||||
onLastPagePress={this.onLastPagePress}
|
||||
onPageSelect={this.onPageSelect}
|
||||
onRemoveSelected={this.onRemoveSelected}
|
||||
onSortPress={this.onSortPress}
|
||||
onTableOptionChange={this.onTableOptionChange}
|
||||
onClearBlacklistPress={this.onClearBlacklistPress}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
BlacklistConnector.propTypes = {
|
||||
useCurrentPage: PropTypes.bool.isRequired,
|
||||
isClearingBlacklistExecuting: PropTypes.bool.isRequired,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
fetchBlacklist: PropTypes.func.isRequired,
|
||||
gotoBlacklistFirstPage: PropTypes.func.isRequired,
|
||||
gotoBlacklistPreviousPage: PropTypes.func.isRequired,
|
||||
gotoBlacklistNextPage: PropTypes.func.isRequired,
|
||||
gotoBlacklistLastPage: PropTypes.func.isRequired,
|
||||
gotoBlacklistPage: PropTypes.func.isRequired,
|
||||
removeBlacklistItems: PropTypes.func.isRequired,
|
||||
setBlacklistSort: PropTypes.func.isRequired,
|
||||
setBlacklistTableOption: PropTypes.func.isRequired,
|
||||
clearBlacklist: PropTypes.func.isRequired,
|
||||
executeCommand: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default withCurrentPage(
|
||||
connect(createMapStateToProps, mapDispatchToProps)(BlacklistConnector)
|
||||
);
|
||||
90
frontend/src/Activity/Blacklist/BlacklistDetailsModal.js
Normal file
@@ -0,0 +1,90 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import DescriptionList from 'Components/DescriptionList/DescriptionList';
|
||||
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
|
||||
import Button from 'Components/Link/Button';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import ModalBody from 'Components/Modal/ModalBody';
|
||||
import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
class BlacklistDetailsModal extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
isOpen,
|
||||
sourceTitle,
|
||||
protocol,
|
||||
indexer,
|
||||
message,
|
||||
onModalClose
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<ModalContent
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<ModalHeader>
|
||||
Details
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('Name')}
|
||||
data={sourceTitle}
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('Protocol')}
|
||||
data={protocol}
|
||||
/>
|
||||
|
||||
{
|
||||
!!message &&
|
||||
<DescriptionListItem
|
||||
title={translate('Indexer')}
|
||||
data={indexer}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
!!message &&
|
||||
<DescriptionListItem
|
||||
title={translate('Message')}
|
||||
data={message}
|
||||
/>
|
||||
}
|
||||
</DescriptionList>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button onPress={onModalClose}>
|
||||
{translate('Close')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
BlacklistDetailsModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
sourceTitle: PropTypes.string.isRequired,
|
||||
protocol: PropTypes.string.isRequired,
|
||||
indexer: PropTypes.string,
|
||||
message: PropTypes.string,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default BlacklistDetailsModal;
|
||||
18
frontend/src/Activity/Blacklist/BlacklistRow.css
Normal file
@@ -0,0 +1,18 @@
|
||||
.language,
|
||||
.quality {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.indexer {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 70px;
|
||||
}
|
||||
213
frontend/src/Activity/Blacklist/BlacklistRow.js
Normal file
@@ -0,0 +1,213 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import MovieFormats from 'Movie/MovieFormats';
|
||||
import MovieLanguage from 'Movie/MovieLanguage';
|
||||
import MovieQuality from 'Movie/MovieQuality';
|
||||
import MovieTitleLink from 'Movie/MovieTitleLink';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import BlacklistDetailsModal from './BlacklistDetailsModal';
|
||||
import styles from './BlacklistRow.css';
|
||||
|
||||
class BlacklistRow extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
isDetailsModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onDetailsPress = () => {
|
||||
this.setState({ isDetailsModalOpen: true });
|
||||
}
|
||||
|
||||
onDetailsModalClose = () => {
|
||||
this.setState({ isDetailsModalOpen: false });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
id,
|
||||
movie,
|
||||
sourceTitle,
|
||||
quality,
|
||||
customFormats,
|
||||
languages,
|
||||
date,
|
||||
protocol,
|
||||
indexer,
|
||||
message,
|
||||
isSelected,
|
||||
columns,
|
||||
onSelectedChange,
|
||||
onRemovePress
|
||||
} = this.props;
|
||||
|
||||
if (!movie) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<TableRow>
|
||||
<TableSelectCell
|
||||
id={id}
|
||||
isSelected={isSelected}
|
||||
onSelectedChange={onSelectedChange}
|
||||
/>
|
||||
|
||||
{
|
||||
columns.map((column) => {
|
||||
const {
|
||||
name,
|
||||
isVisible
|
||||
} = column;
|
||||
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (name === 'movies.sortTitle') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<MovieTitleLink
|
||||
titleSlug={movie.titleSlug}
|
||||
title={movie.title}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'sourceTitle') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
{sourceTitle}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'languages') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<MovieLanguage
|
||||
languages={languages}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'quality') {
|
||||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
className={styles.quality}
|
||||
>
|
||||
<MovieQuality
|
||||
quality={quality}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'customFormats') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<MovieFormats
|
||||
formats={customFormats}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'date') {
|
||||
return (
|
||||
<RelativeDateCellConnector
|
||||
key={name}
|
||||
date={date}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'indexer') {
|
||||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
className={styles.indexer}
|
||||
>
|
||||
{indexer}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'actions') {
|
||||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
className={styles.actions}
|
||||
>
|
||||
<IconButton
|
||||
name={icons.INFO}
|
||||
onPress={this.onDetailsPress}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
title={translate('RemoveFromBlacklist')}
|
||||
name={icons.REMOVE}
|
||||
kind={kinds.DANGER}
|
||||
onPress={onRemovePress}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
}
|
||||
|
||||
<BlacklistDetailsModal
|
||||
isOpen={this.state.isDetailsModalOpen}
|
||||
sourceTitle={sourceTitle}
|
||||
protocol={protocol}
|
||||
indexer={indexer}
|
||||
message={message}
|
||||
onModalClose={this.onDetailsModalClose}
|
||||
/>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BlacklistRow.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
movie: PropTypes.object.isRequired,
|
||||
sourceTitle: PropTypes.string.isRequired,
|
||||
quality: PropTypes.object.isRequired,
|
||||
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
date: PropTypes.string.isRequired,
|
||||
protocol: PropTypes.string.isRequired,
|
||||
indexer: PropTypes.string,
|
||||
message: PropTypes.string,
|
||||
isSelected: PropTypes.bool.isRequired,
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onSelectedChange: PropTypes.func.isRequired,
|
||||
onRemovePress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default BlacklistRow;
|
||||
26
frontend/src/Activity/Blacklist/BlacklistRowConnector.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { removeBlacklistItem } from 'Store/Actions/blacklistActions';
|
||||
import createMovieSelector from 'Store/Selectors/createMovieSelector';
|
||||
import BlacklistRow from './BlacklistRow';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createMovieSelector(),
|
||||
(movie) => {
|
||||
return {
|
||||
movie
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function createMapDispatchToProps(dispatch, props) {
|
||||
return {
|
||||
onRemovePress() {
|
||||
dispatch(removeBlacklistItem({ id: props.id }));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(createMapStateToProps, createMapDispatchToProps)(BlacklistRow);
|
||||
5
frontend/src/Activity/History/Details/HistoryDetails.css
Normal file
@@ -0,0 +1,5 @@
|
||||
.description {
|
||||
composes: description from '~Components/DescriptionList/DescriptionListItemDescription.css';
|
||||
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
279
frontend/src/Activity/History/Details/HistoryDetails.js
Normal file
@@ -0,0 +1,279 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import DescriptionList from 'Components/DescriptionList/DescriptionList';
|
||||
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
|
||||
import DescriptionListItemDescription from 'Components/DescriptionList/DescriptionListItemDescription';
|
||||
import DescriptionListItemTitle from 'Components/DescriptionList/DescriptionListItemTitle';
|
||||
import Link from 'Components/Link/Link';
|
||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||
import formatAge from 'Utilities/Number/formatAge';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './HistoryDetails.css';
|
||||
|
||||
function HistoryDetails(props) {
|
||||
const {
|
||||
eventType,
|
||||
sourceTitle,
|
||||
data,
|
||||
shortDateFormat,
|
||||
timeFormat
|
||||
} = props;
|
||||
|
||||
if (eventType === 'grabbed') {
|
||||
const {
|
||||
indexer,
|
||||
releaseGroup,
|
||||
nzbInfoUrl,
|
||||
downloadClient,
|
||||
downloadId,
|
||||
age,
|
||||
ageHours,
|
||||
ageMinutes,
|
||||
publishedDate
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
descriptionClassName={styles.description}
|
||||
title={translate('Name')}
|
||||
data={sourceTitle}
|
||||
/>
|
||||
|
||||
{
|
||||
!!indexer &&
|
||||
<DescriptionListItem
|
||||
title={translate('Indexer')}
|
||||
data={indexer}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
!!releaseGroup &&
|
||||
<DescriptionListItem
|
||||
descriptionClassName={styles.description}
|
||||
title={translate('ReleaseGroup')}
|
||||
data={releaseGroup}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
!!nzbInfoUrl &&
|
||||
<span>
|
||||
<DescriptionListItemTitle>
|
||||
Info URL
|
||||
</DescriptionListItemTitle>
|
||||
|
||||
<DescriptionListItemDescription>
|
||||
<Link to={nzbInfoUrl}>{nzbInfoUrl}</Link>
|
||||
</DescriptionListItemDescription>
|
||||
</span>
|
||||
}
|
||||
|
||||
{
|
||||
!!downloadClient &&
|
||||
<DescriptionListItem
|
||||
title={translate('DownloadClient')}
|
||||
data={downloadClient}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
!!downloadId &&
|
||||
<DescriptionListItem
|
||||
title={translate('GrabID')}
|
||||
data={downloadId}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
!!indexer &&
|
||||
<DescriptionListItem
|
||||
title={translate('AgeWhenGrabbed')}
|
||||
data={formatAge(age, ageHours, ageMinutes)}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
!!publishedDate &&
|
||||
<DescriptionListItem
|
||||
title={translate('PublishedDate')}
|
||||
data={formatDateTime(publishedDate, shortDateFormat, timeFormat, { includeSeconds: true })}
|
||||
/>
|
||||
}
|
||||
</DescriptionList>
|
||||
);
|
||||
}
|
||||
|
||||
if (eventType === 'downloadFailed') {
|
||||
const {
|
||||
message
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
descriptionClassName={styles.description}
|
||||
title={translate('Name')}
|
||||
data={sourceTitle}
|
||||
/>
|
||||
|
||||
{
|
||||
!!message &&
|
||||
<DescriptionListItem
|
||||
title={translate('Message')}
|
||||
data={message}
|
||||
/>
|
||||
}
|
||||
</DescriptionList>
|
||||
);
|
||||
}
|
||||
|
||||
if (eventType === 'downloadFolderImported') {
|
||||
const {
|
||||
droppedPath,
|
||||
importedPath
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
descriptionClassName={styles.description}
|
||||
title={translate('Name')}
|
||||
data={sourceTitle}
|
||||
/>
|
||||
|
||||
{
|
||||
!!droppedPath &&
|
||||
<DescriptionListItem
|
||||
descriptionClassName={styles.description}
|
||||
title={translate('Source')}
|
||||
data={droppedPath}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
!!importedPath &&
|
||||
<DescriptionListItem
|
||||
descriptionClassName={styles.description}
|
||||
title={translate('ImportedTo')}
|
||||
data={importedPath}
|
||||
/>
|
||||
}
|
||||
</DescriptionList>
|
||||
);
|
||||
}
|
||||
|
||||
if (eventType === 'movieFileDeleted') {
|
||||
const {
|
||||
reason
|
||||
} = data;
|
||||
|
||||
let reasonMessage = '';
|
||||
|
||||
switch (reason) {
|
||||
case 'Manual':
|
||||
reasonMessage = 'File was deleted by via UI';
|
||||
break;
|
||||
case 'MissingFromDisk':
|
||||
reasonMessage = 'Radarr was unable to find the file on disk so it was removed';
|
||||
break;
|
||||
case 'Upgrade':
|
||||
reasonMessage = 'File was deleted to import an upgrade';
|
||||
break;
|
||||
default:
|
||||
reasonMessage = '';
|
||||
}
|
||||
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('Name')}
|
||||
data={sourceTitle}
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('Reason')}
|
||||
data={reasonMessage}
|
||||
/>
|
||||
</DescriptionList>
|
||||
);
|
||||
}
|
||||
|
||||
if (eventType === 'movieFileRenamed') {
|
||||
const {
|
||||
sourcePath,
|
||||
sourceRelativePath,
|
||||
path,
|
||||
relativePath
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('SourcePath')}
|
||||
data={sourcePath}
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('SourceRelativePath')}
|
||||
data={sourceRelativePath}
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('DestinationPath')}
|
||||
data={path}
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('DestinationRelativePath')}
|
||||
data={relativePath}
|
||||
/>
|
||||
</DescriptionList>
|
||||
);
|
||||
}
|
||||
|
||||
if (eventType === 'downloadIgnored') {
|
||||
const {
|
||||
message
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
descriptionClassName={styles.description}
|
||||
title={translate('Name')}
|
||||
data={sourceTitle}
|
||||
/>
|
||||
|
||||
{
|
||||
!!message &&
|
||||
<DescriptionListItem
|
||||
title={translate('Message')}
|
||||
data={message}
|
||||
/>
|
||||
}
|
||||
</DescriptionList>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
descriptionClassName={styles.description}
|
||||
title={translate('Name')}
|
||||
data={sourceTitle}
|
||||
/>
|
||||
</DescriptionList>
|
||||
);
|
||||
}
|
||||
|
||||
HistoryDetails.propTypes = {
|
||||
eventType: PropTypes.string.isRequired,
|
||||
sourceTitle: PropTypes.string.isRequired,
|
||||
data: PropTypes.object.isRequired,
|
||||
shortDateFormat: PropTypes.string.isRequired,
|
||||
timeFormat: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default HistoryDetails;
|
||||
@@ -0,0 +1,18 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import HistoryDetails from './HistoryDetails';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createUISettingsSelector(),
|
||||
(uiSettings) => {
|
||||
return {
|
||||
shortDateFormat: uiSettings.shortDateFormat,
|
||||
timeFormat: uiSettings.timeFormat
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(createMapStateToProps)(HistoryDetails);
|
||||
@@ -0,0 +1,5 @@
|
||||
.markAsFailedButton {
|
||||
composes: button from '~Components/Link/Button.css';
|
||||
|
||||
margin-right: auto;
|
||||
}
|
||||
107
frontend/src/Activity/History/Details/HistoryDetailsModal.js
Normal file
@@ -0,0 +1,107 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Button from 'Components/Link/Button';
|
||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import ModalBody from 'Components/Modal/ModalBody';
|
||||
import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import HistoryDetails from './HistoryDetails';
|
||||
import styles from './HistoryDetailsModal.css';
|
||||
|
||||
function getHeaderTitle(eventType) {
|
||||
switch (eventType) {
|
||||
case 'grabbed':
|
||||
return 'Grabbed';
|
||||
case 'downloadFailed':
|
||||
return 'Download Failed';
|
||||
case 'downloadFolderImported':
|
||||
return 'Movie Imported';
|
||||
case 'movieFileDeleted':
|
||||
return 'Movie File Deleted';
|
||||
case 'movieFileRenamed':
|
||||
return 'Movie File Renamed';
|
||||
case 'downloadIgnored':
|
||||
return 'Download Ignored';
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
function HistoryDetailsModal(props) {
|
||||
const {
|
||||
isOpen,
|
||||
eventType,
|
||||
sourceTitle,
|
||||
data,
|
||||
isMarkingAsFailed,
|
||||
shortDateFormat,
|
||||
timeFormat,
|
||||
onMarkAsFailedPress,
|
||||
onModalClose
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
{getHeaderTitle(eventType)}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<HistoryDetails
|
||||
eventType={eventType}
|
||||
sourceTitle={sourceTitle}
|
||||
data={data}
|
||||
shortDateFormat={shortDateFormat}
|
||||
timeFormat={timeFormat}
|
||||
/>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
{
|
||||
eventType === 'grabbed' &&
|
||||
<SpinnerButton
|
||||
className={styles.markAsFailedButton}
|
||||
kind={kinds.DANGER}
|
||||
isSpinning={isMarkingAsFailed}
|
||||
onPress={onMarkAsFailedPress}
|
||||
>
|
||||
Mark as Failed
|
||||
</SpinnerButton>
|
||||
}
|
||||
|
||||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
{translate('Close')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
HistoryDetailsModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
eventType: PropTypes.string.isRequired,
|
||||
sourceTitle: PropTypes.string.isRequired,
|
||||
data: PropTypes.object.isRequired,
|
||||
isMarkingAsFailed: PropTypes.bool.isRequired,
|
||||
shortDateFormat: PropTypes.string.isRequired,
|
||||
timeFormat: PropTypes.string.isRequired,
|
||||
onMarkAsFailedPress: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
HistoryDetailsModal.defaultProps = {
|
||||
isMarkingAsFailed: false
|
||||
};
|
||||
|
||||
export default HistoryDetailsModal;
|
||||
153
frontend/src/Activity/History/History.js
Normal file
@@ -0,0 +1,153 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import FilterMenu from 'Components/Menu/FilterMenu';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
|
||||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||
import TablePager from 'Components/Table/TablePager';
|
||||
import { align, icons } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import HistoryRowConnector from './HistoryRowConnector';
|
||||
|
||||
class History extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
error,
|
||||
isMoviesFetching,
|
||||
isMoviesPopulated,
|
||||
moviesError,
|
||||
items,
|
||||
columns,
|
||||
selectedFilterKey,
|
||||
filters,
|
||||
totalRecords,
|
||||
onFilterSelect,
|
||||
onFirstPagePress,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
const isFetchingAny = isFetching || isMoviesFetching;
|
||||
const isAllPopulated = isPopulated && (isMoviesPopulated || !items.length);
|
||||
const hasError = error || moviesError;
|
||||
|
||||
return (
|
||||
<PageContent title={translate('History')}>
|
||||
<PageToolbar>
|
||||
<PageToolbarSection>
|
||||
<PageToolbarButton
|
||||
label={translate('Refresh')}
|
||||
iconName={icons.REFRESH}
|
||||
isSpinning={isFetching}
|
||||
onPress={onFirstPagePress}
|
||||
/>
|
||||
</PageToolbarSection>
|
||||
|
||||
<PageToolbarSection alignContent={align.RIGHT}>
|
||||
<TableOptionsModalWrapper
|
||||
{...otherProps}
|
||||
columns={columns}
|
||||
>
|
||||
<PageToolbarButton
|
||||
label={translate('Options')}
|
||||
iconName={icons.TABLE}
|
||||
/>
|
||||
</TableOptionsModalWrapper>
|
||||
|
||||
<FilterMenu
|
||||
alignMenu={align.RIGHT}
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
filters={filters}
|
||||
customFilters={[]}
|
||||
onFilterSelect={onFilterSelect}
|
||||
/>
|
||||
</PageToolbarSection>
|
||||
</PageToolbar>
|
||||
|
||||
<PageContentBody>
|
||||
{
|
||||
isFetchingAny && !isAllPopulated &&
|
||||
<LoadingIndicator />
|
||||
}
|
||||
|
||||
{
|
||||
!isFetchingAny && hasError &&
|
||||
<div>
|
||||
{translate('UnableToLoadHistory')}
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
// If history isPopulated and it's empty show no history found and don't
|
||||
// wait for the episodes to populate because they are never coming.
|
||||
|
||||
isPopulated && !hasError && !items.length &&
|
||||
<div>
|
||||
No history found
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
isAllPopulated && !hasError && !!items.length &&
|
||||
<div>
|
||||
<Table
|
||||
columns={columns}
|
||||
{...otherProps}
|
||||
>
|
||||
<TableBody>
|
||||
{
|
||||
items.map((item) => {
|
||||
return (
|
||||
<HistoryRowConnector
|
||||
key={item.id}
|
||||
columns={columns}
|
||||
{...item}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<TablePager
|
||||
totalRecords={totalRecords}
|
||||
isFetching={isFetching}
|
||||
onFirstPagePress={onFirstPagePress}
|
||||
{...otherProps}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</PageContentBody>
|
||||
</PageContent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
History.propTypes = {
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
isMoviesFetching: PropTypes.bool.isRequired,
|
||||
isMoviesPopulated: PropTypes.bool.isRequired,
|
||||
moviesError: PropTypes.object,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
selectedFilterKey: PropTypes.string.isRequired,
|
||||
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
totalRecords: PropTypes.number,
|
||||
onFilterSelect: PropTypes.func.isRequired,
|
||||
onFirstPagePress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default History;
|
||||
138
frontend/src/Activity/History/HistoryConnector.js
Normal file
@@ -0,0 +1,138 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import withCurrentPage from 'Components/withCurrentPage';
|
||||
import * as historyActions from 'Store/Actions/historyActions';
|
||||
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
|
||||
import History from './History';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.history,
|
||||
(state) => state.movies,
|
||||
(history, movies) => {
|
||||
return {
|
||||
isMoviesFetching: movies.isFetching,
|
||||
isMoviesPopulated: movies.isPopulated,
|
||||
moviesError: movies.error,
|
||||
...history
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
...historyActions
|
||||
};
|
||||
|
||||
class HistoryConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
const {
|
||||
useCurrentPage,
|
||||
fetchHistory,
|
||||
gotoHistoryFirstPage
|
||||
} = this.props;
|
||||
|
||||
registerPagePopulator(this.repopulate);
|
||||
|
||||
if (useCurrentPage) {
|
||||
fetchHistory();
|
||||
} else {
|
||||
gotoHistoryFirstPage();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
unregisterPagePopulator(this.repopulate);
|
||||
this.props.clearHistory();
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
repopulate = () => {
|
||||
this.props.fetchHistory();
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onFirstPagePress = () => {
|
||||
this.props.gotoHistoryFirstPage();
|
||||
}
|
||||
|
||||
onPreviousPagePress = () => {
|
||||
this.props.gotoHistoryPreviousPage();
|
||||
}
|
||||
|
||||
onNextPagePress = () => {
|
||||
this.props.gotoHistoryNextPage();
|
||||
}
|
||||
|
||||
onLastPagePress = () => {
|
||||
this.props.gotoHistoryLastPage();
|
||||
}
|
||||
|
||||
onPageSelect = (page) => {
|
||||
this.props.gotoHistoryPage({ page });
|
||||
}
|
||||
|
||||
onSortPress = (sortKey) => {
|
||||
this.props.setHistorySort({ sortKey });
|
||||
}
|
||||
|
||||
onFilterSelect = (selectedFilterKey) => {
|
||||
this.props.setHistoryFilter({ selectedFilterKey });
|
||||
}
|
||||
|
||||
onTableOptionChange = (payload) => {
|
||||
this.props.setHistoryTableOption(payload);
|
||||
|
||||
if (payload.pageSize) {
|
||||
this.props.gotoHistoryFirstPage();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<History
|
||||
onFirstPagePress={this.onFirstPagePress}
|
||||
onPreviousPagePress={this.onPreviousPagePress}
|
||||
onNextPagePress={this.onNextPagePress}
|
||||
onLastPagePress={this.onLastPagePress}
|
||||
onPageSelect={this.onPageSelect}
|
||||
onSortPress={this.onSortPress}
|
||||
onFilterSelect={this.onFilterSelect}
|
||||
onTableOptionChange={this.onTableOptionChange}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
HistoryConnector.propTypes = {
|
||||
useCurrentPage: PropTypes.bool.isRequired,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
fetchHistory: PropTypes.func.isRequired,
|
||||
gotoHistoryFirstPage: PropTypes.func.isRequired,
|
||||
gotoHistoryPreviousPage: PropTypes.func.isRequired,
|
||||
gotoHistoryNextPage: PropTypes.func.isRequired,
|
||||
gotoHistoryLastPage: PropTypes.func.isRequired,
|
||||
gotoHistoryPage: PropTypes.func.isRequired,
|
||||
setHistorySort: PropTypes.func.isRequired,
|
||||
setHistoryFilter: PropTypes.func.isRequired,
|
||||
setHistoryTableOption: PropTypes.func.isRequired,
|
||||
clearHistory: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default withCurrentPage(
|
||||
connect(createMapStateToProps, mapDispatchToProps)(HistoryConnector)
|
||||
);
|
||||
6
frontend/src/Activity/History/HistoryEventTypeCell.css
Normal file
@@ -0,0 +1,6 @@
|
||||
.cell {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 35px;
|
||||
text-align: center;
|
||||
}
|
||||
86
frontend/src/Activity/History/HistoryEventTypeCell.js
Normal file
@@ -0,0 +1,86 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Icon from 'Components/Icon';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import styles from './HistoryEventTypeCell.css';
|
||||
|
||||
function getIconName(eventType) {
|
||||
switch (eventType) {
|
||||
case 'grabbed':
|
||||
return icons.DOWNLOADING;
|
||||
case 'movieFolderImported':
|
||||
return icons.DRIVE;
|
||||
case 'downloadFolderImported':
|
||||
return icons.DOWNLOADED;
|
||||
case 'downloadFailed':
|
||||
return icons.DOWNLOADING;
|
||||
case 'movieFileDeleted':
|
||||
return icons.DELETE;
|
||||
case 'movieFileRenamed':
|
||||
return icons.ORGANIZE;
|
||||
case 'downloadIgnored':
|
||||
return icons.IGNORE;
|
||||
default:
|
||||
return icons.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
function getIconKind(eventType) {
|
||||
switch (eventType) {
|
||||
case 'downloadFailed':
|
||||
return kinds.DANGER;
|
||||
default:
|
||||
return kinds.DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
function getTooltip(eventType, data) {
|
||||
switch (eventType) {
|
||||
case 'grabbed':
|
||||
return `Movie grabbed from ${data.indexer} and sent to ${data.downloadClient}`;
|
||||
case 'movieFolderImported':
|
||||
return 'Movie imported from movie folder';
|
||||
case 'downloadFolderImported':
|
||||
return 'Movie downloaded successfully and picked up from download client';
|
||||
case 'downloadFailed':
|
||||
return 'Movie download failed';
|
||||
case 'movieFileDeleted':
|
||||
return 'Movie file deleted';
|
||||
case 'movieFileRenamed':
|
||||
return 'Movie file renamed';
|
||||
case 'downloadIgnored':
|
||||
return 'Movie Download Ignored';
|
||||
default:
|
||||
return 'Unknown event';
|
||||
}
|
||||
}
|
||||
|
||||
function HistoryEventTypeCell({ eventType, data }) {
|
||||
const iconName = getIconName(eventType);
|
||||
const iconKind = getIconKind(eventType);
|
||||
const tooltip = getTooltip(eventType, data);
|
||||
|
||||
return (
|
||||
<TableRowCell
|
||||
className={styles.cell}
|
||||
title={tooltip}
|
||||
>
|
||||
<Icon
|
||||
name={iconName}
|
||||
kind={iconKind}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
HistoryEventTypeCell.propTypes = {
|
||||
eventType: PropTypes.string.isRequired,
|
||||
data: PropTypes.object
|
||||
};
|
||||
|
||||
HistoryEventTypeCell.defaultProps = {
|
||||
data: {}
|
||||
};
|
||||
|
||||
export default HistoryEventTypeCell;
|
||||
23
frontend/src/Activity/History/HistoryRow.css
Normal file
@@ -0,0 +1,23 @@
|
||||
.downloadClient {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.indexer {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.releaseGroup {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
.details {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 30px;
|
||||
}
|
||||
236
frontend/src/Activity/History/HistoryRow.js
Normal file
@@ -0,0 +1,236 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import MovieFormats from 'Movie/MovieFormats';
|
||||
import MovieLanguage from 'Movie/MovieLanguage';
|
||||
import MovieQuality from 'Movie/MovieQuality';
|
||||
import MovieTitleLink from 'Movie/MovieTitleLink';
|
||||
import HistoryDetailsModal from './Details/HistoryDetailsModal';
|
||||
import HistoryEventTypeCell from './HistoryEventTypeCell';
|
||||
import styles from './HistoryRow.css';
|
||||
|
||||
class HistoryRow extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
isDetailsModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (
|
||||
prevProps.isMarkingAsFailed &&
|
||||
!this.props.isMarkingAsFailed &&
|
||||
!this.props.markAsFailedError
|
||||
) {
|
||||
this.setState({ isDetailsModalOpen: false });
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onDetailsPress = () => {
|
||||
this.setState({ isDetailsModalOpen: true });
|
||||
}
|
||||
|
||||
onDetailsModalClose = () => {
|
||||
this.setState({ isDetailsModalOpen: false });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
movie,
|
||||
quality,
|
||||
customFormats,
|
||||
languages,
|
||||
qualityCutoffNotMet,
|
||||
eventType,
|
||||
sourceTitle,
|
||||
date,
|
||||
data,
|
||||
isMarkingAsFailed,
|
||||
columns,
|
||||
shortDateFormat,
|
||||
timeFormat,
|
||||
onMarkAsFailedPress
|
||||
} = this.props;
|
||||
|
||||
if (!movie) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<TableRow>
|
||||
{
|
||||
columns.map((column) => {
|
||||
const {
|
||||
name,
|
||||
isVisible
|
||||
} = column;
|
||||
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (name === 'eventType') {
|
||||
return (
|
||||
<HistoryEventTypeCell
|
||||
key={name}
|
||||
eventType={eventType}
|
||||
data={data}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'movies.sortTitle') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<MovieTitleLink
|
||||
titleSlug={movie.titleSlug}
|
||||
title={movie.title}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'languages') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<MovieLanguage
|
||||
languages={languages}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'quality') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<MovieQuality
|
||||
quality={quality}
|
||||
isCutoffMet={qualityCutoffNotMet}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'customFormats') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<MovieFormats
|
||||
formats={customFormats}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'date') {
|
||||
return (
|
||||
<RelativeDateCellConnector
|
||||
key={name}
|
||||
date={date}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'downloadClient') {
|
||||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
className={styles.downloadClient}
|
||||
>
|
||||
{data.downloadClient}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'indexer') {
|
||||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
className={styles.indexer}
|
||||
>
|
||||
{data.indexer}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'releaseGroup') {
|
||||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
className={styles.releaseGroup}
|
||||
>
|
||||
{data.releaseGroup}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'details') {
|
||||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
className={styles.details}
|
||||
>
|
||||
<IconButton
|
||||
name={icons.INFO}
|
||||
onPress={this.onDetailsPress}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
}
|
||||
|
||||
<HistoryDetailsModal
|
||||
isOpen={this.state.isDetailsModalOpen}
|
||||
eventType={eventType}
|
||||
sourceTitle={sourceTitle}
|
||||
data={data}
|
||||
isMarkingAsFailed={isMarkingAsFailed}
|
||||
shortDateFormat={shortDateFormat}
|
||||
timeFormat={timeFormat}
|
||||
onMarkAsFailedPress={onMarkAsFailedPress}
|
||||
onModalClose={this.onDetailsModalClose}
|
||||
/>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
HistoryRow.propTypes = {
|
||||
movieId: PropTypes.number,
|
||||
movie: PropTypes.object.isRequired,
|
||||
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
quality: PropTypes.object.isRequired,
|
||||
qualityCutoffNotMet: PropTypes.bool.isRequired,
|
||||
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
eventType: PropTypes.string.isRequired,
|
||||
sourceTitle: PropTypes.string.isRequired,
|
||||
date: PropTypes.string.isRequired,
|
||||
data: PropTypes.object.isRequired,
|
||||
isMarkingAsFailed: PropTypes.bool,
|
||||
markAsFailedError: PropTypes.object,
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
shortDateFormat: PropTypes.string.isRequired,
|
||||
timeFormat: PropTypes.string.isRequired,
|
||||
onMarkAsFailedPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default HistoryRow;
|
||||
73
frontend/src/Activity/History/HistoryRowConnector.js
Normal file
@@ -0,0 +1,73 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { fetchHistory, markAsFailed } from 'Store/Actions/historyActions';
|
||||
import createMovieSelector from 'Store/Selectors/createMovieSelector';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import HistoryRow from './HistoryRow';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createMovieSelector(),
|
||||
createUISettingsSelector(),
|
||||
(movie, uiSettings) => {
|
||||
return {
|
||||
movie,
|
||||
shortDateFormat: uiSettings.shortDateFormat,
|
||||
timeFormat: uiSettings.timeFormat
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchHistory,
|
||||
markAsFailed
|
||||
};
|
||||
|
||||
class HistoryRowConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (
|
||||
prevProps.isMarkingAsFailed &&
|
||||
!this.props.isMarkingAsFailed &&
|
||||
!this.props.markAsFailedError
|
||||
) {
|
||||
this.props.fetchHistory();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onMarkAsFailedPress = () => {
|
||||
this.props.markAsFailed({ id: this.props.id });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<HistoryRow
|
||||
{...this.props}
|
||||
onMarkAsFailedPress={this.onMarkAsFailedPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
HistoryRowConnector.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
isMarkingAsFailed: PropTypes.bool,
|
||||
markAsFailedError: PropTypes.object,
|
||||
fetchHistory: PropTypes.func.isRequired,
|
||||
markAsFailed: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(HistoryRowConnector);
|
||||
13
frontend/src/Activity/Queue/ProtocolLabel.css
Normal file
@@ -0,0 +1,13 @@
|
||||
.torrent {
|
||||
composes: label from '~Components/Label.css';
|
||||
|
||||
border-color: $torrentColor;
|
||||
background-color: $torrentColor;
|
||||
}
|
||||
|
||||
.usenet {
|
||||
composes: label from '~Components/Label.css';
|
||||
|
||||
border-color: $usenetColor;
|
||||
background-color: $usenetColor;
|
||||
}
|
||||
20
frontend/src/Activity/Queue/ProtocolLabel.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Label from 'Components/Label';
|
||||
import styles from './ProtocolLabel.css';
|
||||
|
||||
function ProtocolLabel({ protocol }) {
|
||||
const protocolName = protocol === 'usenet' ? 'nzb' : protocol;
|
||||
|
||||
return (
|
||||
<Label className={styles[protocol]}>
|
||||
{protocolName}
|
||||
</Label>
|
||||
);
|
||||
}
|
||||
|
||||
ProtocolLabel.propTypes = {
|
||||
protocol: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default ProtocolLabel;
|
||||
292
frontend/src/Activity/Queue/Queue.js
Normal file
@@ -0,0 +1,292 @@
|
||||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
|
||||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||
import TablePager from 'Components/Table/TablePager';
|
||||
import { align, icons } from 'Helpers/Props';
|
||||
import getRemovedItems from 'Utilities/Object/getRemovedItems';
|
||||
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
||||
import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState';
|
||||
import selectAll from 'Utilities/Table/selectAll';
|
||||
import toggleSelected from 'Utilities/Table/toggleSelected';
|
||||
import QueueOptionsConnector from './QueueOptionsConnector';
|
||||
import QueueRowConnector from './QueueRowConnector';
|
||||
import RemoveQueueItemsModal from './RemoveQueueItemsModal';
|
||||
|
||||
class Queue extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
allSelected: false,
|
||||
allUnselected: false,
|
||||
lastToggled: null,
|
||||
selectedState: {},
|
||||
isPendingSelected: false,
|
||||
isConfirmRemoveModalOpen: false,
|
||||
items: props.items
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
items,
|
||||
isFetching,
|
||||
isMoviesFetching
|
||||
} = this.props;
|
||||
|
||||
if (
|
||||
(!isMoviesFetching && prevProps.isMoviesFetching) ||
|
||||
(!isFetching && prevProps.isFetching) ||
|
||||
(hasDifferentItems(prevProps.items, items) && !items.some((e) => e.movieId))
|
||||
) {
|
||||
this.setState((state) => {
|
||||
return {
|
||||
...removeOldSelectedState(state, getRemovedItems(prevProps.items, items)),
|
||||
items
|
||||
};
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedIds = this.getSelectedIds();
|
||||
const isPendingSelected = _.some(this.props.items, (item) => {
|
||||
return selectedIds.indexOf(item.id) > -1 && item.status === 'delay';
|
||||
});
|
||||
|
||||
if (isPendingSelected !== this.state.isPendingSelected) {
|
||||
this.setState({ isPendingSelected });
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
getSelectedIds = () => {
|
||||
return getSelectedIds(this.state.selectedState);
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onSelectAllChange = ({ value }) => {
|
||||
this.setState(selectAll(this.state.selectedState, value));
|
||||
}
|
||||
|
||||
onSelectedChange = ({ id, value, shiftKey = false }) => {
|
||||
this.setState((state) => {
|
||||
return toggleSelected(state, this.props.items, id, value, shiftKey);
|
||||
});
|
||||
}
|
||||
|
||||
onGrabSelectedPress = () => {
|
||||
this.props.onGrabSelectedPress(this.getSelectedIds());
|
||||
}
|
||||
|
||||
onRemoveSelectedPress = () => {
|
||||
this.setState({ isConfirmRemoveModalOpen: true });
|
||||
}
|
||||
|
||||
onRemoveSelectedConfirmed = (payload) => {
|
||||
this.props.onRemoveSelectedPress({ ids: this.getSelectedIds(), ...payload });
|
||||
this.setState({ isConfirmRemoveModalOpen: false });
|
||||
}
|
||||
|
||||
onConfirmRemoveModalClose = () => {
|
||||
this.setState({ isConfirmRemoveModalOpen: false });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
error,
|
||||
isMoviesFetching,
|
||||
isMoviesPopulated,
|
||||
moviesError,
|
||||
columns,
|
||||
totalRecords,
|
||||
isGrabbing,
|
||||
isRemoving,
|
||||
isRefreshMonitoredDownloadsExecuting,
|
||||
onRefreshPress,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
allSelected,
|
||||
allUnselected,
|
||||
selectedState,
|
||||
isConfirmRemoveModalOpen,
|
||||
isPendingSelected,
|
||||
items
|
||||
} = this.state;
|
||||
|
||||
const isRefreshing = isFetching || isMoviesFetching || isRefreshMonitoredDownloadsExecuting;
|
||||
const isAllPopulated = isPopulated && (isMoviesPopulated || !items.length || items.every((e) => !e.movieId));
|
||||
const hasError = error || moviesError;
|
||||
const selectedIds = this.getSelectedIds();
|
||||
const selectedCount = selectedIds.length;
|
||||
const disableSelectedActions = selectedCount === 0;
|
||||
|
||||
return (
|
||||
<PageContent title={translate('Queue')}>
|
||||
<PageToolbar>
|
||||
<PageToolbarSection>
|
||||
<PageToolbarButton
|
||||
label={translate('Refresh')}
|
||||
iconName={icons.REFRESH}
|
||||
isSpinning={isRefreshing}
|
||||
onPress={onRefreshPress}
|
||||
/>
|
||||
|
||||
<PageToolbarSeparator />
|
||||
|
||||
<PageToolbarButton
|
||||
label={translate('GrabSelected')}
|
||||
iconName={icons.DOWNLOAD}
|
||||
isDisabled={disableSelectedActions || !isPendingSelected}
|
||||
isSpinning={isGrabbing}
|
||||
onPress={this.onGrabSelectedPress}
|
||||
/>
|
||||
|
||||
<PageToolbarButton
|
||||
label={translate('RemoveSelected')}
|
||||
iconName={icons.REMOVE}
|
||||
isDisabled={disableSelectedActions}
|
||||
isSpinning={isRemoving}
|
||||
onPress={this.onRemoveSelectedPress}
|
||||
/>
|
||||
</PageToolbarSection>
|
||||
|
||||
<PageToolbarSection
|
||||
alignContent={align.RIGHT}
|
||||
>
|
||||
<TableOptionsModalWrapper
|
||||
columns={columns}
|
||||
{...otherProps}
|
||||
optionsComponent={QueueOptionsConnector}
|
||||
>
|
||||
<PageToolbarButton
|
||||
label={translate('Options')}
|
||||
iconName={icons.TABLE}
|
||||
/>
|
||||
</TableOptionsModalWrapper>
|
||||
</PageToolbarSection>
|
||||
</PageToolbar>
|
||||
|
||||
<PageContentBody>
|
||||
{
|
||||
isRefreshing && !isAllPopulated &&
|
||||
<LoadingIndicator />
|
||||
}
|
||||
|
||||
{
|
||||
!isRefreshing && hasError &&
|
||||
<div>
|
||||
Failed to load Queue
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
isPopulated && !hasError && !items.length &&
|
||||
<div>
|
||||
Queue is empty
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
isAllPopulated && !hasError && !!items.length &&
|
||||
<div>
|
||||
<Table
|
||||
columns={columns}
|
||||
selectAll={true}
|
||||
allSelected={allSelected}
|
||||
allUnselected={allUnselected}
|
||||
{...otherProps}
|
||||
optionsComponent={QueueOptionsConnector}
|
||||
onSelectAllChange={this.onSelectAllChange}
|
||||
>
|
||||
<TableBody>
|
||||
{
|
||||
items.map((item) => {
|
||||
return (
|
||||
<QueueRowConnector
|
||||
key={item.id}
|
||||
movieId={item.movieId}
|
||||
isSelected={selectedState[item.id]}
|
||||
columns={columns}
|
||||
{...item}
|
||||
onSelectedChange={this.onSelectedChange}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<TablePager
|
||||
totalRecords={totalRecords}
|
||||
isFetching={isRefreshing}
|
||||
{...otherProps}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</PageContentBody>
|
||||
|
||||
<RemoveQueueItemsModal
|
||||
isOpen={isConfirmRemoveModalOpen}
|
||||
selectedCount={selectedCount}
|
||||
canIgnore={isConfirmRemoveModalOpen && (
|
||||
selectedIds.every((id) => {
|
||||
const item = items.find((i) => i.id === id);
|
||||
|
||||
return !!(item && item.movieId);
|
||||
})
|
||||
)}
|
||||
onRemovePress={this.onRemoveSelectedConfirmed}
|
||||
onModalClose={this.onConfirmRemoveModalClose}
|
||||
/>
|
||||
</PageContent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Queue.propTypes = {
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
isMoviesFetching: PropTypes.bool.isRequired,
|
||||
isMoviesPopulated: PropTypes.bool.isRequired,
|
||||
moviesError: PropTypes.object,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
totalRecords: PropTypes.number,
|
||||
isGrabbing: PropTypes.bool.isRequired,
|
||||
isRemoving: PropTypes.bool.isRequired,
|
||||
isRefreshMonitoredDownloadsExecuting: PropTypes.bool.isRequired,
|
||||
onRefreshPress: PropTypes.func.isRequired,
|
||||
onGrabSelectedPress: PropTypes.func.isRequired,
|
||||
onRemoveSelectedPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default Queue;
|
||||
170
frontend/src/Activity/Queue/QueueConnector.js
Normal file
@@ -0,0 +1,170 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import withCurrentPage from 'Components/withCurrentPage';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import * as queueActions from 'Store/Actions/queueActions';
|
||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
|
||||
import Queue from './Queue';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.movies,
|
||||
(state) => state.queue.options,
|
||||
(state) => state.queue.paged,
|
||||
createCommandExecutingSelector(commandNames.REFRESH_MONITORED_DOWNLOADS),
|
||||
(movies, options, queue, isRefreshMonitoredDownloadsExecuting) => {
|
||||
return {
|
||||
isMoviesFetching: movies.isFetching,
|
||||
isMoviesPopulated: movies.isPopulated,
|
||||
moviesError: movies.error,
|
||||
isRefreshMonitoredDownloadsExecuting,
|
||||
...options,
|
||||
...queue
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
...queueActions,
|
||||
executeCommand
|
||||
};
|
||||
|
||||
class QueueConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
const {
|
||||
useCurrentPage,
|
||||
fetchQueue,
|
||||
gotoQueueFirstPage
|
||||
} = this.props;
|
||||
|
||||
registerPagePopulator(this.repopulate);
|
||||
|
||||
if (useCurrentPage) {
|
||||
fetchQueue();
|
||||
} else {
|
||||
gotoQueueFirstPage();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (
|
||||
this.props.includeUnknownMovieItems !==
|
||||
prevProps.includeUnknownMovieItems
|
||||
) {
|
||||
this.repopulate();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
unregisterPagePopulator(this.repopulate);
|
||||
this.props.clearQueue();
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
repopulate = () => {
|
||||
this.props.fetchQueue();
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onFirstPagePress = () => {
|
||||
this.props.gotoQueueFirstPage();
|
||||
}
|
||||
|
||||
onPreviousPagePress = () => {
|
||||
this.props.gotoQueuePreviousPage();
|
||||
}
|
||||
|
||||
onNextPagePress = () => {
|
||||
this.props.gotoQueueNextPage();
|
||||
}
|
||||
|
||||
onLastPagePress = () => {
|
||||
this.props.gotoQueueLastPage();
|
||||
}
|
||||
|
||||
onPageSelect = (page) => {
|
||||
this.props.gotoQueuePage({ page });
|
||||
}
|
||||
|
||||
onSortPress = (sortKey) => {
|
||||
this.props.setQueueSort({ sortKey });
|
||||
}
|
||||
|
||||
onTableOptionChange = (payload) => {
|
||||
this.props.setQueueTableOption(payload);
|
||||
|
||||
if (payload.pageSize) {
|
||||
this.props.gotoQueueFirstPage();
|
||||
}
|
||||
}
|
||||
|
||||
onRefreshPress = () => {
|
||||
this.props.executeCommand({
|
||||
name: commandNames.REFRESH_MONITORED_DOWNLOADS
|
||||
});
|
||||
}
|
||||
|
||||
onGrabSelectedPress = (ids) => {
|
||||
this.props.grabQueueItems({ ids });
|
||||
}
|
||||
|
||||
onRemoveSelectedPress = (payload) => {
|
||||
this.props.removeQueueItems(payload);
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Queue
|
||||
onFirstPagePress={this.onFirstPagePress}
|
||||
onPreviousPagePress={this.onPreviousPagePress}
|
||||
onNextPagePress={this.onNextPagePress}
|
||||
onLastPagePress={this.onLastPagePress}
|
||||
onPageSelect={this.onPageSelect}
|
||||
onSortPress={this.onSortPress}
|
||||
onTableOptionChange={this.onTableOptionChange}
|
||||
onRefreshPress={this.onRefreshPress}
|
||||
onGrabSelectedPress={this.onGrabSelectedPress}
|
||||
onRemoveSelectedPress={this.onRemoveSelectedPress}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
QueueConnector.propTypes = {
|
||||
includeUnknownMovieItems: PropTypes.bool.isRequired,
|
||||
useCurrentPage: PropTypes.bool.isRequired,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
fetchQueue: PropTypes.func.isRequired,
|
||||
gotoQueueFirstPage: PropTypes.func.isRequired,
|
||||
gotoQueuePreviousPage: PropTypes.func.isRequired,
|
||||
gotoQueueNextPage: PropTypes.func.isRequired,
|
||||
gotoQueueLastPage: PropTypes.func.isRequired,
|
||||
gotoQueuePage: PropTypes.func.isRequired,
|
||||
setQueueSort: PropTypes.func.isRequired,
|
||||
setQueueTableOption: PropTypes.func.isRequired,
|
||||
clearQueue: PropTypes.func.isRequired,
|
||||
grabQueueItems: PropTypes.func.isRequired,
|
||||
removeQueueItems: PropTypes.func.isRequired,
|
||||
executeCommand: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default withCurrentPage(
|
||||
connect(createMapStateToProps, mapDispatchToProps)(QueueConnector)
|
||||
);
|
||||
128
frontend/src/Activity/Queue/QueueDetails.js
Normal file
@@ -0,0 +1,128 @@
|
||||
import moment from 'moment';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Icon from 'Components/Icon';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
function QueueDetails(props) {
|
||||
const {
|
||||
title,
|
||||
size,
|
||||
sizeleft,
|
||||
estimatedCompletionTime,
|
||||
status,
|
||||
trackedDownloadState,
|
||||
trackedDownloadStatus,
|
||||
errorMessage,
|
||||
progressBar
|
||||
} = props;
|
||||
|
||||
const progress = size ? (100 - sizeleft / size * 100) : 0;
|
||||
|
||||
if (status === 'pending') {
|
||||
return (
|
||||
<Icon
|
||||
name={icons.PENDING}
|
||||
title={translate('ReleaseWillBeProcessedInterp', [moment(estimatedCompletionTime).fromNow()])}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (status === 'completed') {
|
||||
if (errorMessage) {
|
||||
return (
|
||||
<Icon
|
||||
name={icons.DOWNLOAD}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('ImportFailedInterp', [errorMessage])}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (trackedDownloadStatus === 'warning') {
|
||||
return (
|
||||
<Icon
|
||||
name={icons.DOWNLOAD}
|
||||
kind={kinds.WARNING}
|
||||
title={'Downloaded - Unable to Import: check logs for details'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (trackedDownloadState === 'importPending') {
|
||||
return (
|
||||
<Icon
|
||||
name={icons.DOWNLOAD}
|
||||
kind={kinds.PURPLE}
|
||||
title={`${translate('Downloaded')} - ${translate('WaitingToImport')}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (trackedDownloadState === 'importing') {
|
||||
return (
|
||||
<Icon
|
||||
name={icons.DOWNLOAD}
|
||||
kind={kinds.PURPLE}
|
||||
title={`${translate('Downloaded')} - ${translate('Importing')}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (errorMessage) {
|
||||
return (
|
||||
<Icon
|
||||
name={icons.DOWNLOADING}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DownloadFailedInterp', [errorMessage])}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (status === 'failed') {
|
||||
return (
|
||||
<Icon
|
||||
name={icons.DOWNLOADING}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DownloadFailedCheckDownloadClientForMoreDetails')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (status === 'warning') {
|
||||
return (
|
||||
<Icon
|
||||
name={icons.DOWNLOADING}
|
||||
kind={kinds.WARNING}
|
||||
title={translate('DownloadWarningCheckDownloadClientForMoreDetails')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (progress < 5) {
|
||||
return (
|
||||
<Icon
|
||||
name={icons.DOWNLOADING}
|
||||
title={translate('MovieIsDownloadingInterp', [progress.toFixed(1), title])}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return progressBar;
|
||||
}
|
||||
|
||||
QueueDetails.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
size: PropTypes.number.isRequired,
|
||||
sizeleft: PropTypes.number.isRequired,
|
||||
estimatedCompletionTime: PropTypes.string,
|
||||
status: PropTypes.string.isRequired,
|
||||
trackedDownloadState: PropTypes.string.isRequired,
|
||||
trackedDownloadStatus: PropTypes.string.isRequired,
|
||||
errorMessage: PropTypes.string,
|
||||
progressBar: PropTypes.node.isRequired
|
||||
};
|
||||
|
||||
export default QueueDetails;
|
||||
78
frontend/src/Activity/Queue/QueueOptions.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import FormGroup from 'Components/Form/FormGroup';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import { inputTypes } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
class QueueOptions extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
includeUnknownMovieItems: props.includeUnknownMovieItems
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
includeUnknownMovieItems
|
||||
} = this.props;
|
||||
|
||||
if (includeUnknownMovieItems !== prevProps.includeUnknownMovieItems) {
|
||||
this.setState({
|
||||
includeUnknownMovieItems
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onOptionChange = ({ name, value }) => {
|
||||
this.setState({
|
||||
[name]: value
|
||||
}, () => {
|
||||
this.props.onOptionChange({
|
||||
[name]: value
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
includeUnknownMovieItems
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('ShowUnknownMovieItems')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="includeUnknownMovieItems"
|
||||
value={includeUnknownMovieItems}
|
||||
helpText={translate('IncludeUnknownMovieItemsHelpText')}
|
||||
onChange={this.onOptionChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
QueueOptions.propTypes = {
|
||||
includeUnknownMovieItems: PropTypes.bool.isRequired,
|
||||
onOptionChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default QueueOptions;
|
||||
19
frontend/src/Activity/Queue/QueueOptionsConnector.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { setQueueOption } from 'Store/Actions/queueActions';
|
||||
import QueueOptions from './QueueOptions';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.queue.options,
|
||||
(options) => {
|
||||
return options;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
onOptionChange: setQueueOption
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(QueueOptions);
|
||||
23
frontend/src/Activity/Queue/QueueRow.css
Normal file
@@ -0,0 +1,23 @@
|
||||
.quality {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.protocol {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.progress {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 70px;
|
||||
}
|
||||
370
frontend/src/Activity/Queue/QueueRow.js
Normal file
@@ -0,0 +1,370 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import ProtocolLabel from 'Activity/Queue/ProtocolLabel';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
||||
import ProgressBar from 'Components/ProgressBar';
|
||||
// import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
|
||||
import MovieFormats from 'Movie/MovieFormats';
|
||||
import MovieLanguage from 'Movie/MovieLanguage';
|
||||
import MovieQuality from 'Movie/MovieQuality';
|
||||
import MovieTitleLink from 'Movie/MovieTitleLink';
|
||||
import formatBytes from 'Utilities/Number/formatBytes';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import QueueStatusCell from './QueueStatusCell';
|
||||
import RemoveQueueItemModal from './RemoveQueueItemModal';
|
||||
import TimeleftCell from './TimeleftCell';
|
||||
import styles from './QueueRow.css';
|
||||
|
||||
class QueueRow extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
isRemoveQueueItemModalOpen: false,
|
||||
isInteractiveImportModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onRemoveQueueItemPress = () => {
|
||||
this.setState({ isRemoveQueueItemModalOpen: true });
|
||||
}
|
||||
|
||||
onRemoveQueueItemModalConfirmed = (blacklist) => {
|
||||
this.props.onRemoveQueueItemPress(blacklist);
|
||||
this.setState({ isRemoveQueueItemModalOpen: false });
|
||||
}
|
||||
|
||||
onRemoveQueueItemModalClose = () => {
|
||||
this.setState({ isRemoveQueueItemModalOpen: false });
|
||||
}
|
||||
|
||||
onInteractiveImportPress = () => {
|
||||
this.setState({ isInteractiveImportModalOpen: true });
|
||||
}
|
||||
|
||||
onInteractiveImportModalClose = () => {
|
||||
this.setState({ isInteractiveImportModalOpen: false });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
id,
|
||||
downloadId,
|
||||
title,
|
||||
status,
|
||||
trackedDownloadStatus,
|
||||
trackedDownloadState,
|
||||
statusMessages,
|
||||
errorMessage,
|
||||
movie,
|
||||
quality,
|
||||
customFormats,
|
||||
languages,
|
||||
protocol,
|
||||
indexer,
|
||||
outputPath,
|
||||
downloadClient,
|
||||
estimatedCompletionTime,
|
||||
timeleft,
|
||||
size,
|
||||
sizeleft,
|
||||
showRelativeDates,
|
||||
shortDateFormat,
|
||||
timeFormat,
|
||||
isGrabbing,
|
||||
grabError,
|
||||
isRemoving,
|
||||
isSelected,
|
||||
columns,
|
||||
onSelectedChange,
|
||||
onGrabPress
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
isRemoveQueueItemModalOpen,
|
||||
isInteractiveImportModalOpen
|
||||
} = this.state;
|
||||
|
||||
const progress = 100 - (sizeleft / size * 100);
|
||||
const showInteractiveImport = status === 'completed' && trackedDownloadStatus === 'warning';
|
||||
const isPending = status === 'delay' || status === 'downloadClientUnavailable';
|
||||
|
||||
return (
|
||||
<TableRow>
|
||||
<TableSelectCell
|
||||
id={id}
|
||||
isSelected={isSelected}
|
||||
onSelectedChange={onSelectedChange}
|
||||
/>
|
||||
|
||||
{
|
||||
columns.map((column) => {
|
||||
const {
|
||||
name,
|
||||
isVisible
|
||||
} = column;
|
||||
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (name === 'status') {
|
||||
return (
|
||||
<QueueStatusCell
|
||||
key={name}
|
||||
sourceTitle={title}
|
||||
status={status}
|
||||
trackedDownloadStatus={trackedDownloadStatus}
|
||||
trackedDownloadState={trackedDownloadState}
|
||||
statusMessages={statusMessages}
|
||||
errorMessage={errorMessage}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'movies.sortTitle') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
{
|
||||
movie ?
|
||||
<MovieTitleLink
|
||||
titleSlug={movie.titleSlug}
|
||||
title={movie.title}
|
||||
/> :
|
||||
title
|
||||
}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'languages') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<MovieLanguage
|
||||
languages={languages}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'quality') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
{
|
||||
quality ?
|
||||
<MovieQuality
|
||||
quality={quality}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'customFormats') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<MovieFormats
|
||||
formats={customFormats}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'protocol') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<ProtocolLabel
|
||||
protocol={protocol}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'indexer') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
{indexer}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'downloadClient') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
{downloadClient}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'size') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
{formatBytes(size)}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'title') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
{title}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'outputPath') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
{outputPath}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'estimatedCompletionTime') {
|
||||
return (
|
||||
<TimeleftCell
|
||||
key={name}
|
||||
status={status}
|
||||
estimatedCompletionTime={estimatedCompletionTime}
|
||||
timeleft={timeleft}
|
||||
size={size}
|
||||
sizeleft={sizeleft}
|
||||
showRelativeDates={showRelativeDates}
|
||||
shortDateFormat={shortDateFormat}
|
||||
timeFormat={timeFormat}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'progress') {
|
||||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
className={styles.progress}
|
||||
>
|
||||
{
|
||||
!!progress &&
|
||||
<ProgressBar
|
||||
progress={progress}
|
||||
title={`${progress.toFixed(1)}%`}
|
||||
/>
|
||||
}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'actions') {
|
||||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
className={styles.actions}
|
||||
>
|
||||
{
|
||||
showInteractiveImport &&
|
||||
<IconButton
|
||||
name={icons.INTERACTIVE}
|
||||
onPress={this.onInteractiveImportPress}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
isPending &&
|
||||
<SpinnerIconButton
|
||||
name={icons.DOWNLOAD}
|
||||
kind={grabError ? kinds.DANGER : kinds.DEFAULT}
|
||||
isSpinning={isGrabbing}
|
||||
onPress={onGrabPress}
|
||||
/>
|
||||
}
|
||||
|
||||
<SpinnerIconButton
|
||||
title={translate('RemoveFromQueue')}
|
||||
name={icons.REMOVE}
|
||||
isSpinning={isRemoving}
|
||||
onPress={this.onRemoveQueueItemPress}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
}
|
||||
|
||||
<InteractiveImportModal
|
||||
isOpen={isInteractiveImportModalOpen}
|
||||
downloadId={downloadId}
|
||||
title={title}
|
||||
onModalClose={this.onInteractiveImportModalClose}
|
||||
/>
|
||||
|
||||
<RemoveQueueItemModal
|
||||
isOpen={isRemoveQueueItemModalOpen}
|
||||
sourceTitle={title}
|
||||
canIgnore={!!movie}
|
||||
onRemovePress={this.onRemoveQueueItemModalConfirmed}
|
||||
onModalClose={this.onRemoveQueueItemModalClose}
|
||||
/>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QueueRow.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
downloadId: PropTypes.string,
|
||||
title: PropTypes.string.isRequired,
|
||||
status: PropTypes.string.isRequired,
|
||||
trackedDownloadStatus: PropTypes.string,
|
||||
trackedDownloadState: PropTypes.string,
|
||||
statusMessages: PropTypes.arrayOf(PropTypes.object),
|
||||
errorMessage: PropTypes.string,
|
||||
movie: PropTypes.object,
|
||||
quality: PropTypes.object.isRequired,
|
||||
customFormats: PropTypes.arrayOf(PropTypes.object),
|
||||
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
protocol: PropTypes.string.isRequired,
|
||||
indexer: PropTypes.string,
|
||||
outputPath: PropTypes.string,
|
||||
downloadClient: PropTypes.string,
|
||||
estimatedCompletionTime: PropTypes.string,
|
||||
timeleft: PropTypes.string,
|
||||
size: PropTypes.number,
|
||||
sizeleft: PropTypes.number,
|
||||
showRelativeDates: PropTypes.bool.isRequired,
|
||||
shortDateFormat: PropTypes.string.isRequired,
|
||||
timeFormat: PropTypes.string.isRequired,
|
||||
isGrabbing: PropTypes.bool.isRequired,
|
||||
grabError: PropTypes.object,
|
||||
isRemoving: PropTypes.bool.isRequired,
|
||||
isSelected: PropTypes.bool,
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onSelectedChange: PropTypes.func.isRequired,
|
||||
onGrabPress: PropTypes.func.isRequired,
|
||||
onRemoveQueueItemPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
QueueRow.defaultProps = {
|
||||
isGrabbing: false,
|
||||
isRemoving: false
|
||||
};
|
||||
|
||||
export default QueueRow;
|
||||
67
frontend/src/Activity/Queue/QueueRowConnector.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { grabQueueItem, removeQueueItem } from 'Store/Actions/queueActions';
|
||||
import createMovieSelector from 'Store/Selectors/createMovieSelector';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import QueueRow from './QueueRow';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createMovieSelector(),
|
||||
createUISettingsSelector(),
|
||||
(movie, uiSettings) => {
|
||||
const result = {
|
||||
showRelativeDates: uiSettings.showRelativeDates,
|
||||
shortDateFormat: uiSettings.shortDateFormat,
|
||||
timeFormat: uiSettings.timeFormat
|
||||
};
|
||||
|
||||
result.movie = movie;
|
||||
|
||||
return result;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
grabQueueItem,
|
||||
removeQueueItem
|
||||
};
|
||||
|
||||
class QueueRowConnector extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onGrabPress = () => {
|
||||
this.props.grabQueueItem({ id: this.props.id });
|
||||
}
|
||||
|
||||
onRemoveQueueItemPress = (payload) => {
|
||||
this.props.removeQueueItem({ id: this.props.id, ...payload });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<QueueRow
|
||||
{...this.props}
|
||||
onGrabPress={this.onGrabPress}
|
||||
onRemoveQueueItemPress={this.onRemoveQueueItemPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
QueueRowConnector.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
movie: PropTypes.object,
|
||||
grabQueueItem: PropTypes.func.isRequired,
|
||||
removeQueueItem: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(QueueRowConnector);
|
||||
5
frontend/src/Activity/Queue/QueueStatusCell.css
Normal file
@@ -0,0 +1,5 @@
|
||||
.status {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 30px;
|
||||
}
|
||||
157
frontend/src/Activity/Queue/QueueStatusCell.js
Normal file
@@ -0,0 +1,157 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Icon from 'Components/Icon';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import Popover from 'Components/Tooltip/Popover';
|
||||
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './QueueStatusCell.css';
|
||||
|
||||
function getDetailedPopoverBody(statusMessages) {
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
statusMessages.map(({ title, messages }) => {
|
||||
return (
|
||||
<div key={title}>
|
||||
{title}
|
||||
<ul>
|
||||
{
|
||||
messages.map((message) => {
|
||||
return (
|
||||
<li key={message}>
|
||||
{message}
|
||||
</li>
|
||||
);
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function QueueStatusCell(props) {
|
||||
const {
|
||||
sourceTitle,
|
||||
status,
|
||||
trackedDownloadStatus,
|
||||
trackedDownloadState,
|
||||
statusMessages,
|
||||
errorMessage
|
||||
} = props;
|
||||
|
||||
const hasWarning = trackedDownloadStatus === 'warning';
|
||||
const hasError = trackedDownloadStatus === 'error';
|
||||
|
||||
// status === 'downloading'
|
||||
let iconName = icons.DOWNLOADING;
|
||||
let iconKind = kinds.DEFAULT;
|
||||
let title = translate('Downloading');
|
||||
|
||||
if (status === 'paused') {
|
||||
iconName = icons.PAUSED;
|
||||
title = translate('Paused');
|
||||
}
|
||||
|
||||
if (status === 'queued') {
|
||||
iconName = icons.QUEUED;
|
||||
title = translate('Queued');
|
||||
}
|
||||
|
||||
if (status === 'completed') {
|
||||
iconName = icons.DOWNLOADED;
|
||||
title = translate('Downloaded');
|
||||
|
||||
if (trackedDownloadState === 'importPending') {
|
||||
title += ` - ${translate('WaitingToImport')}`;
|
||||
iconKind = kinds.PURPLE;
|
||||
}
|
||||
|
||||
if (trackedDownloadState === 'importing') {
|
||||
title += ` - ${translate('Importing')}`;
|
||||
iconKind = kinds.PURPLE;
|
||||
}
|
||||
|
||||
if (trackedDownloadState === 'failedPending') {
|
||||
title += ` - ${translate('WaitingToProcess')}`;
|
||||
iconKind = kinds.DANGER;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasWarning) {
|
||||
iconKind = kinds.WARNING;
|
||||
}
|
||||
|
||||
if (status === 'delay') {
|
||||
iconName = icons.PENDING;
|
||||
title = translate('Pending');
|
||||
}
|
||||
|
||||
if (status === 'DownloadClientUnavailable') {
|
||||
iconName = icons.PENDING;
|
||||
iconKind = kinds.WARNING;
|
||||
title = `${translate('Pending')} - ${translate('DownloadClientUnavailable')}`;
|
||||
}
|
||||
|
||||
if (status === 'failed') {
|
||||
iconName = icons.DOWNLOADING;
|
||||
iconKind = kinds.DANGER;
|
||||
title = translate('DownloadFailed');
|
||||
}
|
||||
|
||||
if (status === 'warning') {
|
||||
iconName = icons.DOWNLOADING;
|
||||
iconKind = kinds.WARNING;
|
||||
const warningMessage = errorMessage || translate('CheckDownloadClientForDetails');
|
||||
title = translate('DownloadWarning', [warningMessage]);
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
if (status === 'completed') {
|
||||
iconName = icons.DOWNLOAD;
|
||||
iconKind = kinds.DANGER;
|
||||
title = translate('ImportFailed', [sourceTitle]);
|
||||
} else {
|
||||
iconName = icons.DOWNLOADING;
|
||||
iconKind = kinds.DANGER;
|
||||
title = translate('DownloadFailed');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<TableRowCell className={styles.status}>
|
||||
<Popover
|
||||
anchor={
|
||||
<Icon
|
||||
name={iconName}
|
||||
kind={iconKind}
|
||||
/>
|
||||
}
|
||||
title={title}
|
||||
body={hasWarning || hasError ? getDetailedPopoverBody(statusMessages) : sourceTitle}
|
||||
position={tooltipPositions.RIGHT}
|
||||
canFlip={false}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
QueueStatusCell.propTypes = {
|
||||
sourceTitle: PropTypes.string.isRequired,
|
||||
status: PropTypes.string.isRequired,
|
||||
trackedDownloadStatus: PropTypes.string.isRequired,
|
||||
trackedDownloadState: PropTypes.string.isRequired,
|
||||
statusMessages: PropTypes.arrayOf(PropTypes.object),
|
||||
errorMessage: PropTypes.string
|
||||
};
|
||||
|
||||
QueueStatusCell.defaultProps = {
|
||||
trackedDownloadStatus: translate('Ok'),
|
||||
trackedDownloadState: translate('Downloading')
|
||||
};
|
||||
|
||||
export default QueueStatusCell;
|
||||
144
frontend/src/Activity/Queue/RemoveQueueItemModal.js
Normal file
@@ -0,0 +1,144 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import FormGroup from 'Components/Form/FormGroup';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import Button from 'Components/Link/Button';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import ModalBody from 'Components/Modal/ModalBody';
|
||||
import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
class RemoveQueueItemModal extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
remove: true,
|
||||
blacklist: false
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
resetState = function() {
|
||||
this.setState({
|
||||
remove: true,
|
||||
blacklist: false
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onRemoveChange = ({ value }) => {
|
||||
this.setState({ remove: value });
|
||||
}
|
||||
|
||||
onBlacklistChange = ({ value }) => {
|
||||
this.setState({ blacklist: value });
|
||||
}
|
||||
|
||||
onRemoveConfirmed = () => {
|
||||
const state = this.state;
|
||||
|
||||
this.resetState();
|
||||
this.props.onRemovePress(state);
|
||||
}
|
||||
|
||||
onModalClose = () => {
|
||||
this.resetState();
|
||||
this.props.onModalClose();
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
isOpen,
|
||||
sourceTitle,
|
||||
canIgnore
|
||||
} = this.props;
|
||||
|
||||
const { remove, blacklist } = this.state;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
size={sizes.MEDIUM}
|
||||
onModalClose={this.onModalClose}
|
||||
>
|
||||
<ModalContent
|
||||
onModalClose={this.onModalClose}
|
||||
>
|
||||
<ModalHeader>
|
||||
Remove - {sourceTitle}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<div>
|
||||
Are you sure you want to remove '{sourceTitle}' from the queue?
|
||||
</div>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('RemoveFromDownloadClient')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="remove"
|
||||
value={remove}
|
||||
helpTextWarning={translate('RemoveHelpTextWarning')}
|
||||
isDisabled={!canIgnore}
|
||||
onChange={this.onRemoveChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('BlacklistRelease')}</FormLabel>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="blacklist"
|
||||
value={blacklist}
|
||||
helpText={translate('BlacklistHelpText')}
|
||||
onChange={this.onBlacklistChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button onPress={this.onModalClose}>
|
||||
{translate('Close')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
kind={kinds.DANGER}
|
||||
onPress={this.onRemoveConfirmed}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
RemoveQueueItemModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
sourceTitle: PropTypes.string.isRequired,
|
||||
canIgnore: PropTypes.bool.isRequired,
|
||||
onRemovePress: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default RemoveQueueItemModal;
|
||||
3
frontend/src/Activity/Queue/RemoveQueueItemsModal.css
Normal file
@@ -0,0 +1,3 @@
|
||||
.message {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
148
frontend/src/Activity/Queue/RemoveQueueItemsModal.js
Normal file
@@ -0,0 +1,148 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import FormGroup from 'Components/Form/FormGroup';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import Button from 'Components/Link/Button';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import ModalBody from 'Components/Modal/ModalBody';
|
||||
import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './RemoveQueueItemsModal.css';
|
||||
|
||||
class RemoveQueueItemsModal extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
remove: true,
|
||||
blacklist: false
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
resetState = function() {
|
||||
this.setState({
|
||||
remove: true,
|
||||
blacklist: false
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onRemoveChange = ({ value }) => {
|
||||
this.setState({ remove: value });
|
||||
}
|
||||
|
||||
onBlacklistChange = ({ value }) => {
|
||||
this.setState({ blacklist: value });
|
||||
}
|
||||
|
||||
onRemoveConfirmed = () => {
|
||||
const state = this.state;
|
||||
|
||||
this.resetState();
|
||||
this.props.onRemovePress(state);
|
||||
}
|
||||
|
||||
onModalClose = () => {
|
||||
this.resetState();
|
||||
this.props.onModalClose();
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
isOpen,
|
||||
selectedCount,
|
||||
canIgnore
|
||||
} = this.props;
|
||||
|
||||
const { remove, blacklist } = this.state;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
size={sizes.MEDIUM}
|
||||
onModalClose={this.onModalClose}
|
||||
>
|
||||
<ModalContent
|
||||
onModalClose={this.onModalClose}
|
||||
>
|
||||
<ModalHeader>
|
||||
Remove Selected Item{selectedCount > 1 ? 's' : ''}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<div className={styles.message}>
|
||||
Are you sure you want to remove {selectedCount} item{selectedCount > 1 ? 's' : ''} from the queue?
|
||||
</div>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('RemoveFromDownloadClient')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="remove"
|
||||
value={remove}
|
||||
helpTextWarning={translate('RemoveHelpTextWarning')}
|
||||
isDisabled={!canIgnore}
|
||||
onChange={this.onRemoveChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
Blacklist Release{selectedCount > 1 ? 's' : ''}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="blacklist"
|
||||
value={blacklist}
|
||||
helpText={translate('BlacklistHelpText')}
|
||||
onChange={this.onBlacklistChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button onPress={this.onModalClose}>
|
||||
{translate('Close')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
kind={kinds.DANGER}
|
||||
onPress={this.onRemoveConfirmed}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
RemoveQueueItemsModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
selectedCount: PropTypes.number.isRequired,
|
||||
canIgnore: PropTypes.bool.isRequired,
|
||||
onRemovePress: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default RemoveQueueItemsModal;
|
||||
76
frontend/src/Activity/Queue/Status/QueueStatusConnector.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import PageSidebarStatus from 'Components/Page/Sidebar/PageSidebarStatus';
|
||||
import { fetchQueueStatus } from 'Store/Actions/queueActions';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.app,
|
||||
(state) => state.queue.status,
|
||||
(state) => state.queue.options.includeUnknownMovieItems,
|
||||
(app, status, includeUnknownMovieItems) => {
|
||||
const {
|
||||
errors,
|
||||
warnings,
|
||||
unknownErrors,
|
||||
unknownWarnings,
|
||||
count,
|
||||
totalCount
|
||||
} = status.item;
|
||||
|
||||
return {
|
||||
isConnected: app.isConnected,
|
||||
isReconnecting: app.isReconnecting,
|
||||
isPopulated: status.isPopulated,
|
||||
...status.item,
|
||||
count: includeUnknownMovieItems ? totalCount : count,
|
||||
errors: includeUnknownMovieItems ? errors || unknownErrors : errors,
|
||||
warnings: includeUnknownMovieItems ? warnings || unknownWarnings : warnings
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchQueueStatus
|
||||
};
|
||||
|
||||
class QueueStatusConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
if (!this.props.isPopulated) {
|
||||
this.props.fetchQueueStatus();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.isConnected && prevProps.isReconnecting) {
|
||||
this.props.fetchQueueStatus();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<PageSidebarStatus
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
QueueStatusConnector.propTypes = {
|
||||
isConnected: PropTypes.bool.isRequired,
|
||||
isReconnecting: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
fetchQueueStatus: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(QueueStatusConnector);
|
||||
5
frontend/src/Activity/Queue/TimeleftCell.css
Normal file
@@ -0,0 +1,5 @@
|
||||
.timeleft {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 100px;
|
||||
}
|
||||
83
frontend/src/Activity/Queue/TimeleftCell.js
Normal file
@@ -0,0 +1,83 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import formatTime from 'Utilities/Date/formatTime';
|
||||
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
||||
import getRelativeDate from 'Utilities/Date/getRelativeDate';
|
||||
import formatBytes from 'Utilities/Number/formatBytes';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './TimeleftCell.css';
|
||||
|
||||
function TimeleftCell(props) {
|
||||
const {
|
||||
estimatedCompletionTime,
|
||||
timeleft,
|
||||
status,
|
||||
size,
|
||||
sizeleft,
|
||||
showRelativeDates,
|
||||
shortDateFormat,
|
||||
timeFormat
|
||||
} = props;
|
||||
|
||||
if (status === 'delay') {
|
||||
const date = getRelativeDate(estimatedCompletionTime, shortDateFormat, showRelativeDates);
|
||||
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
|
||||
|
||||
return (
|
||||
<TableRowCell
|
||||
className={styles.timeleft}
|
||||
title={translate('DelayingDownloadUntilInterp', [date, time])}
|
||||
>
|
||||
-
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (status === 'downloadClientUnavailable') {
|
||||
const date = getRelativeDate(estimatedCompletionTime, shortDateFormat, showRelativeDates);
|
||||
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
|
||||
|
||||
return (
|
||||
<TableRowCell
|
||||
className={styles.timeleft}
|
||||
title={translate('RetryingDownloadInterp', [date, time])}
|
||||
>
|
||||
-
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (!timeleft || status === 'completed' || status === 'failed') {
|
||||
return (
|
||||
<TableRowCell className={styles.timeleft}>
|
||||
-
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
const totalSize = formatBytes(size);
|
||||
const remainingSize = formatBytes(sizeleft);
|
||||
|
||||
return (
|
||||
<TableRowCell
|
||||
className={styles.timeleft}
|
||||
title={`${remainingSize} / ${totalSize}`}
|
||||
>
|
||||
{formatTimeSpan(timeleft)}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
TimeleftCell.propTypes = {
|
||||
estimatedCompletionTime: PropTypes.string,
|
||||
timeleft: PropTypes.string,
|
||||
status: PropTypes.string.isRequired,
|
||||
size: PropTypes.number.isRequired,
|
||||
sizeleft: PropTypes.number.isRequired,
|
||||
showRelativeDates: PropTypes.bool.isRequired,
|
||||
shortDateFormat: PropTypes.string.isRequired,
|
||||
timeFormat: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default TimeleftCell;
|
||||
60
frontend/src/AddMovie/AddNewMovie/AddNewMovie.css
Normal file
@@ -0,0 +1,60 @@
|
||||
.searchContainer {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.searchIconContainer {
|
||||
width: 58px;
|
||||
height: 46px;
|
||||
border: 1px solid $inputBorderColor;
|
||||
border-right: none;
|
||||
border-radius: 4px;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
background-color: #edf1f2;
|
||||
text-align: center;
|
||||
line-height: 46px;
|
||||
}
|
||||
|
||||
.searchInput {
|
||||
composes: input from '~Components/Form/TextInput.css';
|
||||
|
||||
height: 46px;
|
||||
border-radius: 0;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.clearLookupButton {
|
||||
border: 1px solid $inputBorderColor;
|
||||
border-left: none;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-top: 30px;
|
||||
text-align: center;
|
||||
font-weight: 300;
|
||||
font-size: $largeFontSize;
|
||||
}
|
||||
|
||||
.helpText {
|
||||
margin-bottom: 10px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.noMoviesText {
|
||||
margin-top: 80px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.noResults {
|
||||
margin-bottom: 10px;
|
||||
font-weight: 300;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.searchResults {
|
||||
margin-top: 30px;
|
||||
}
|
||||