Compare commits
686 Commits
v1.5.0
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd70ab8711 | ||
|
|
51b8794e4d | ||
|
|
0cfd1fa724 | ||
|
|
b5b297e99a | ||
|
|
58414cadae | ||
|
|
462794520e | ||
|
|
59afba007d | ||
|
|
cfa303e4f3 | ||
|
|
d3dde9c7eb | ||
|
|
9c94ef6de0 | ||
|
|
2665106847 | ||
|
|
d029177fc0 | ||
|
|
1698c404cd | ||
|
|
abac894a95 | ||
|
|
12854ff661 | ||
|
|
3d52a87302 | ||
|
|
9ee5cc6895 | ||
|
|
c45c9e5873 | ||
|
|
24fcba6bee | ||
|
|
d5994a9037 | ||
|
|
9e0d0c03c0 | ||
|
|
70ff8f6ace | ||
|
|
e347219721 | ||
|
|
72a626cb1a | ||
|
|
a1f3f92c10 | ||
|
|
caaa08fbf4 | ||
|
|
5db60141bb | ||
|
|
96b84d45db | ||
|
|
311e625eee | ||
|
|
e22e0394bd | ||
|
|
6377aa8b83 | ||
|
|
8855204930 | ||
|
|
7a13326d87 | ||
|
|
15e9cd001f | ||
|
|
e52b84c3c7 | ||
|
|
84a430651f | ||
|
|
463e6914e3 | ||
|
|
e5e85077ae | ||
|
|
a39e0d204a | ||
|
|
dd0b439bbe | ||
|
|
732b9c0970 | ||
|
|
57028991df | ||
|
|
d5c41430a6 | ||
|
|
5db3560e9a | ||
|
|
13ed291dd4 | ||
|
|
fd2befd2a1 | ||
|
|
940c1ea2d1 | ||
|
|
9c7679101f | ||
|
|
75406cbc64 | ||
|
|
805a6981a6 | ||
|
|
acd883df21 | ||
|
|
f2141ca8b8 | ||
|
|
7182344036 | ||
|
|
a34b0f11a6 | ||
|
|
40812c6040 | ||
|
|
29476e2008 | ||
|
|
29a0d8fe5d | ||
|
|
55b2dd63d8 | ||
|
|
6e46b482cb | ||
|
|
7824828bea | ||
|
|
5368cad77a | ||
|
|
1857cf501c | ||
|
|
a7bb5e1e04 | ||
|
|
ff013ac76e | ||
|
|
42502c93fc | ||
|
|
eac58d2843 | ||
|
|
97cb3b5c1e | ||
|
|
7ef4e52c81 | ||
|
|
2c36adfd75 | ||
|
|
0f272052b3 | ||
|
|
0b4b84a4aa | ||
|
|
9ff2dc4b92 | ||
|
|
593e1828cc | ||
|
|
1b2d99922c | ||
|
|
e647c99722 | ||
|
|
3cd750b882 | ||
|
|
87f10e2508 | ||
|
|
fc691f95bb | ||
|
|
7e334ffbb1 | ||
|
|
1f12a7cc3b | ||
|
|
3ffc28b723 | ||
|
|
21495653d2 | ||
|
|
2674d8e018 | ||
|
|
30ccd0e128 | ||
|
|
e6daba279c | ||
|
|
76d9d47a44 | ||
|
|
5caf9169bf | ||
|
|
54b99a67e5 | ||
|
|
be3f66c817 | ||
|
|
93711ed1d9 | ||
|
|
bd7e9090b2 | ||
|
|
fc79563be3 | ||
|
|
4de6ab9acb | ||
|
|
d9d6f944e8 | ||
|
|
ea30abf1a1 | ||
|
|
6f8e73c90e | ||
|
|
063072f25c | ||
|
|
4ea4275440 | ||
|
|
d420a2714c | ||
|
|
e785b871df | ||
|
|
f144ce1bdf | ||
|
|
2d2afaba50 | ||
|
|
a253ffd946 | ||
|
|
3937441e84 | ||
|
|
4841d1ed11 | ||
|
|
8f6b4ec36c | ||
|
|
64f01cc919 | ||
|
|
edb2001cdc | ||
|
|
5f7aa928ed | ||
|
|
88464e76dd | ||
|
|
02480f1420 | ||
|
|
3f500eccc6 | ||
|
|
dd1e5d6621 | ||
|
|
dc7cc98a1a | ||
|
|
a695a5e3ee | ||
|
|
86130f0a6e | ||
|
|
e14f19bd45 | ||
|
|
7c488e4fae | ||
|
|
95c22fa854 | ||
|
|
40130c6a37 | ||
|
|
90c2a1d65b | ||
|
|
51560ae1e9 | ||
|
|
009f743259 | ||
|
|
8de3a71bfd | ||
|
|
35880231e0 | ||
|
|
d3e12579ac | ||
|
|
57c94dbd57 | ||
|
|
3c67705275 | ||
|
|
11ff6cd981 | ||
|
|
499d6a2b3f | ||
|
|
ef0662d584 | ||
|
|
63a65c85bd | ||
|
|
4cc4ff3629 | ||
|
|
9e6851d055 | ||
|
|
a508b7dabd | ||
|
|
12ce02473d | ||
|
|
530ff01caf | ||
|
|
1f53f51be7 | ||
|
|
c2724659d9 | ||
|
|
8b8559a50f | ||
|
|
4f74a5bee6 | ||
|
|
7ea1984d6c | ||
|
|
8c380da3b4 | ||
|
|
733c7f78de | ||
|
|
0da3e53bcb | ||
|
|
6993ec8752 | ||
|
|
cf7de07120 | ||
|
|
91ed5e34dc | ||
|
|
b05c255a53 | ||
|
|
a29e53bbe5 | ||
|
|
db54f3b2f2 | ||
|
|
0a3e7a973f | ||
|
|
09cad60673 | ||
|
|
b510b1a6e4 | ||
|
|
edb6aab77d | ||
|
|
e723fdae96 | ||
|
|
6eb8979bed | ||
|
|
4763a5a771 | ||
|
|
61fc87e577 | ||
|
|
4cd320a5c2 | ||
|
|
28e3707e47 | ||
|
|
e405c9f8c2 | ||
|
|
e4cb7d55cb | ||
|
|
9c6676f1f3 | ||
|
|
ff3797e1c4 | ||
|
|
3b920135c2 | ||
|
|
0c30ede537 | ||
|
|
db37e7373e | ||
|
|
b66410142d | ||
|
|
242efa744f | ||
|
|
fabcc26095 | ||
|
|
e1a7c5fe5d | ||
|
|
688ca8ab9e | ||
|
|
c7c6158a3f | ||
|
|
b87a23b5b4 | ||
|
|
f184def9ad | ||
|
|
4e8dbdd128 | ||
|
|
cc6e77deb7 | ||
|
|
a974a3484b | ||
|
|
bf84cc0a06 | ||
|
|
7a44463982 | ||
|
|
0d45eae6e8 | ||
|
|
888186a084 | ||
|
|
e5efeaddf9 | ||
|
|
4b7d5eea54 | ||
|
|
9187617530 | ||
|
|
2493b43d4f | ||
|
|
45208105c0 | ||
|
|
c56bf705ac | ||
|
|
8c584626f1 | ||
|
|
2b21915b1f | ||
|
|
3574a0a08e | ||
|
|
e21e74a9ea | ||
|
|
4fc033828e | ||
|
|
7d7f7b4fd5 | ||
|
|
e0fdca082c | ||
|
|
1293cc692c | ||
|
|
6b2f426ff9 | ||
|
|
e0a04bb040 | ||
|
|
b854a13338 | ||
|
|
5c8cff00a9 | ||
|
|
12fe84017c | ||
|
|
ecc030238e | ||
|
|
ed78bde604 | ||
|
|
d5c649d5bf | ||
|
|
cfe34358a0 | ||
|
|
1f50b18b9f | ||
|
|
b20d2ed09d | ||
|
|
eb7a15d32d | ||
|
|
4bb3064779 | ||
|
|
edcc238cdd | ||
|
|
c78bc05627 | ||
|
|
bfd9196f5e | ||
|
|
1cfc90e4ac | ||
|
|
322b868996 | ||
|
|
2e95798e04 | ||
|
|
1edb2cae9b | ||
|
|
bcd1ccfd21 | ||
|
|
63ff29f024 | ||
|
|
bfa13762d5 | ||
|
|
a2462aa964 | ||
|
|
7f5363ffb2 | ||
|
|
157e8ee77d | ||
|
|
c3e9eec5f7 | ||
|
|
0443981d5f | ||
|
|
224644f9af | ||
|
|
32def988ad | ||
|
|
a40077a5b1 | ||
|
|
6ed3c91bde | ||
|
|
90dafbd2e5 | ||
|
|
1afa13a954 | ||
|
|
28915a5484 | ||
|
|
380de78b51 | ||
|
|
796de41fd7 | ||
|
|
c1d8810614 | ||
|
|
f4178a7792 | ||
|
|
ec077c7f26 | ||
|
|
56fa5bed17 | ||
|
|
18dd20d93e | ||
|
|
d14aae1fa0 | ||
|
|
f71f7f3efd | ||
|
|
db044d9351 | ||
|
|
e12ff1d67b | ||
|
|
c1f733dd9b | ||
|
|
a098b172ca | ||
|
|
5271acb51d | ||
|
|
b3a10df70c | ||
|
|
fda59e49b2 | ||
|
|
8695d26ad9 | ||
|
|
68f9d71bfa | ||
|
|
1caff77412 | ||
|
|
266d81688c | ||
|
|
53091e7204 | ||
|
|
2a8573a2ba | ||
|
|
6d025b835d | ||
|
|
c96f5b8f40 | ||
|
|
c0ef386905 | ||
|
|
45e934a260 | ||
|
|
014f239e19 | ||
|
|
30a07691a2 | ||
|
|
a55c58da2b | ||
|
|
23041cbd09 | ||
|
|
5a274f58b8 | ||
|
|
7fbf80d1fd | ||
|
|
703c11271d | ||
|
|
4dfb0bbadb | ||
|
|
eda1f9c13d | ||
|
|
c37f406074 | ||
|
|
2d80198d6c | ||
|
|
dda9be13bb | ||
|
|
c984d40fd3 | ||
|
|
8f2f016b61 | ||
|
|
1ddcef0676 | ||
|
|
fe3b77a889 | ||
|
|
51f05c4a8a | ||
|
|
807ed1b17b | ||
|
|
b7030c4483 | ||
|
|
647b211ef7 | ||
|
|
509c937b0d | ||
|
|
84879600d0 | ||
|
|
7f4d73ed9d | ||
|
|
9c529bd9d0 | ||
|
|
06dadd29b0 | ||
|
|
b819d3b1d9 | ||
|
|
a603c65fd3 | ||
|
|
d479cc84f3 | ||
|
|
bed2fe8fa7 | ||
|
|
53fbc703bf | ||
|
|
643d2448b1 | ||
|
|
9f6d43d7de | ||
|
|
6ab897b899 | ||
|
|
1963af4da8 | ||
|
|
e5c0690470 | ||
|
|
5610398e15 | ||
|
|
63f77124cb | ||
|
|
c17efaa306 | ||
|
|
1d1e678890 | ||
|
|
d680099a0b | ||
|
|
fd457049af | ||
|
|
20100441d2 | ||
|
|
7aabc307c5 | ||
|
|
3128c68ecb | ||
|
|
417de8b289 | ||
|
|
c8347f19e9 | ||
|
|
2a1bd7199d | ||
|
|
9bdb711195 | ||
|
|
902f471917 | ||
|
|
161f9ca06a | ||
|
|
4c60037a17 | ||
|
|
d2e8d5eeb7 | ||
|
|
ec93c1abc4 | ||
|
|
5cdfb96717 | ||
|
|
b5a7263489 | ||
|
|
dadd850524 | ||
|
|
1f7dd795d1 | ||
|
|
087f4b9f15 | ||
|
|
13b32e7104 | ||
|
|
aca60cd9b7 | ||
|
|
bfe4b05ca3 | ||
|
|
d65e00206d | ||
|
|
247a562f7b | ||
|
|
9bbd6ee6df | ||
|
|
64bcd85287 | ||
|
|
579ceb6e87 | ||
|
|
fdaaf099cb | ||
|
|
8e719ef94f | ||
|
|
1b03a57c3e | ||
|
|
1f60db20f3 | ||
|
|
5200821b60 | ||
|
|
92b9bff892 | ||
|
|
30f5ecd997 | ||
|
|
e117bed738 | ||
|
|
961c89e4e2 | ||
|
|
d1eee14b79 | ||
|
|
e522fa9801 | ||
|
|
11cafa800a | ||
|
|
bdeb60ab30 | ||
|
|
24b735439c | ||
|
|
4eb78bd495 | ||
|
|
751c4558a2 | ||
|
|
d8948e4462 | ||
|
|
0554fee85f | ||
|
|
ffb86324c4 | ||
|
|
83932d6c99 | ||
|
|
9e76fc9b8b | ||
|
|
a36b161a38 | ||
|
|
3ac8b7588a | ||
|
|
c477e3d656 | ||
|
|
ff0eae31dc | ||
|
|
5e3afee676 | ||
|
|
606109d629 | ||
|
|
cc9fecc09a | ||
|
|
a85cf9f8af | ||
|
|
85738a13e6 | ||
|
|
bb5c61ac79 | ||
|
|
9b0fac3a8b | ||
|
|
af13c0eb59 | ||
|
|
e62f6d4511 | ||
|
|
31a23dd781 | ||
|
|
4ea70a4f2e | ||
|
|
252f8c2f65 | ||
|
|
f9bceffa18 | ||
|
|
5871400986 | ||
|
|
a741baded5 | ||
|
|
c38995579c | ||
|
|
ecdfc38470 | ||
|
|
e8563b25e2 | ||
|
|
70ac88b2c2 | ||
|
|
ea8c72ed8e | ||
|
|
780edf1aff | ||
|
|
b89320f25d | ||
|
|
bc59eac490 | ||
|
|
92fdac1f7f | ||
|
|
a5c55cc591 | ||
|
|
45fc771c16 | ||
|
|
faee405c8d | ||
|
|
2f35cebce3 | ||
|
|
8e9947652d | ||
|
|
9932331123 | ||
|
|
910b49a66d | ||
|
|
c68462396b | ||
|
|
568eaa88fe | ||
|
|
e2f3cfd173 | ||
|
|
221a4c3506 | ||
|
|
8a04a7aa41 | ||
|
|
c71ba85c1c | ||
|
|
106190d7ac | ||
|
|
32aae83875 | ||
|
|
7c7c1b90ea | ||
|
|
8346ed9fa5 | ||
|
|
571dcd1cf8 | ||
|
|
d460bc10c8 | ||
|
|
68bb500fe7 | ||
|
|
724d358107 | ||
|
|
aabfe24063 | ||
|
|
855385b097 | ||
|
|
bbebbe92d9 | ||
|
|
790eca9858 | ||
|
|
6f4423e501 | ||
|
|
079ac1b130 | ||
|
|
22c4ddc869 | ||
|
|
3173803ac8 | ||
|
|
abfae4f1f3 | ||
|
|
19c3300342 | ||
|
|
3fb85c766f | ||
|
|
1a1d38aa29 | ||
|
|
e01402d992 | ||
|
|
82700abeb6 | ||
|
|
49f8886db1 | ||
|
|
8053867db3 | ||
|
|
39c88a0519 | ||
|
|
30b710e618 | ||
|
|
931f06e825 | ||
|
|
bae3906063 | ||
|
|
ee7cbe2866 | ||
|
|
fdffa38c92 | ||
|
|
9ac16e5ab9 | ||
|
|
bb566e6eb9 | ||
|
|
b846480b84 | ||
|
|
994e558090 | ||
|
|
89965e65e1 | ||
|
|
6c2ebc6c4c | ||
|
|
702e89b8b5 | ||
|
|
fd434ec544 | ||
|
|
8540149a97 | ||
|
|
b5ac036103 | ||
|
|
3dac3caa68 | ||
|
|
0cae36dcac | ||
|
|
18c97c3526 | ||
|
|
f62394fdb7 | ||
|
|
91df5cec26 | ||
|
|
5910b047b3 | ||
|
|
b53e9fde9b | ||
|
|
7eb4ff8902 | ||
|
|
98ee8d265d | ||
|
|
b6ff4e917f | ||
|
|
124357ec9c | ||
|
|
3d00809634 | ||
|
|
b59d42f0eb | ||
|
|
bebd55a211 | ||
|
|
ed6271ab35 | ||
|
|
16014392a1 | ||
|
|
f6dea2b8be | ||
|
|
3b3aaed05d | ||
|
|
a941dee22f | ||
|
|
c02061a99f | ||
|
|
3a553d32e6 | ||
|
|
d099c7aab9 | ||
|
|
9e91f71bd1 | ||
|
|
ec2897b134 | ||
|
|
8f5cc9329c | ||
|
|
1c81083e23 | ||
|
|
d8b81ded43 | ||
|
|
9125bddf12 | ||
|
|
0ac34c0834 | ||
|
|
6203ec5ce0 | ||
|
|
6f8d3eea4e | ||
|
|
d6c1a03d78 | ||
|
|
3eaa3dd233 | ||
|
|
340186a3fc | ||
|
|
786b14527c | ||
|
|
a012a0cc67 | ||
|
|
1f544e55e8 | ||
|
|
c367e3d898 | ||
|
|
c9ecc9c5b4 | ||
|
|
43206971d8 | ||
|
|
ee7d514c13 | ||
|
|
a397264944 | ||
|
|
62da720944 | ||
|
|
ef634af4ba | ||
|
|
e4b8596468 | ||
|
|
36643bb6d1 | ||
|
|
18c0b38c8d | ||
|
|
ded9503169 | ||
|
|
a599832c00 | ||
|
|
d34ac0e0b5 | ||
|
|
4f6829ae9c | ||
|
|
871b6133f3 | ||
|
|
cd5b16a4e5 | ||
|
|
5a71246623 | ||
|
|
ea6ece3286 | ||
|
|
b147bb8434 | ||
|
|
6c4b6a52cf | ||
|
|
35a4b4a14c | ||
|
|
41531b3807 | ||
|
|
b2973cf2f2 | ||
|
|
dfa58b8f71 | ||
|
|
82edbe73d8 | ||
|
|
611e066473 | ||
|
|
cd549cfc84 | ||
|
|
f9747816db | ||
|
|
1a489dc71d | ||
|
|
174c675992 | ||
|
|
d381fdddce | ||
|
|
79ccfe701d | ||
|
|
2eaa1b94c0 | ||
|
|
0cd9cc8851 | ||
|
|
9f2ec53d23 | ||
|
|
35824af5d4 | ||
|
|
7035507a57 | ||
|
|
f755589258 | ||
|
|
56a215de97 | ||
|
|
fb095e61cb | ||
|
|
8cf62004a2 | ||
|
|
60f129af3a | ||
|
|
9fcb7b1d20 | ||
|
|
233044a429 | ||
|
|
0046fc473c | ||
|
|
29ee6ed53b | ||
|
|
9f9431dbaf | ||
|
|
89f4a76825 | ||
|
|
2bcec538a3 | ||
|
|
35de9629ec | ||
|
|
a095e1987f | ||
|
|
f8f21df913 | ||
|
|
818dd4428f | ||
|
|
405f807c1e | ||
|
|
1862707ae3 | ||
|
|
c8d0ec2a5f | ||
|
|
6c20f7f026 | ||
|
|
e2c65c9231 | ||
|
|
9c84e04de2 | ||
|
|
369ae87f88 | ||
|
|
c773bf41b6 | ||
|
|
99c0a7dafa | ||
|
|
bb294225f2 | ||
|
|
a83a7d8c08 | ||
|
|
73e2ecefeb | ||
|
|
799d7ea240 | ||
|
|
0ac029d5c9 | ||
|
|
6cdb2dbfe3 | ||
|
|
68c1634ef5 | ||
|
|
ace5ac3f9b | ||
|
|
575efddf53 | ||
|
|
e6319c8893 | ||
|
|
61c6f50c5c | ||
|
|
7886d86c72 | ||
|
|
7e6b4652f2 | ||
|
|
afe42d084d | ||
|
|
2905651bd7 | ||
|
|
7f431d29e9 | ||
|
|
fb3e64359f | ||
|
|
80a58d4a02 | ||
|
|
0abc09f130 | ||
|
|
9735fce4ae | ||
|
|
ed2ff16dd6 | ||
|
|
b2635cc650 | ||
|
|
d3c8e6ea1f | ||
|
|
4a3dea3cab | ||
|
|
c56aebd85d | ||
|
|
558d18163c | ||
|
|
a8b962f450 | ||
|
|
efab99473a | ||
|
|
038cfdd079 | ||
|
|
6610a01769 | ||
|
|
ad08be52a7 | ||
|
|
e7863c7a6c | ||
|
|
be97c5133c | ||
|
|
73496f9450 | ||
|
|
64873d9eee | ||
|
|
1507044809 | ||
|
|
97d0e140b7 | ||
|
|
7c9b8d53fd | ||
|
|
04371a2a50 | ||
|
|
1fca7b083a | ||
|
|
b9dfb07cf7 | ||
|
|
e5a69d521b | ||
|
|
d82c2efbe5 | ||
|
|
d59b4a1ec1 | ||
|
|
a2982e12c0 | ||
|
|
3e5f43a5d3 | ||
|
|
6a273da054 | ||
|
|
7c2048b32f | ||
|
|
d8b0ee0001 | ||
|
|
f38c825105 | ||
|
|
06db92feac | ||
|
|
507560f8c3 | ||
|
|
7731016868 | ||
|
|
c4deda96fb | ||
|
|
8f31841130 | ||
|
|
608bdf32e7 | ||
|
|
6224d418be | ||
|
|
9eb2411bce | ||
|
|
cc554164c3 | ||
|
|
f8e7b80a9b | ||
|
|
b404ab13e7 | ||
|
|
97b894a9a0 | ||
|
|
2c02f50c86 | ||
|
|
a3e3d6a30d | ||
|
|
6ed86616aa | ||
|
|
d75621bb22 | ||
|
|
c35042ade8 | ||
|
|
7721ea6fab | ||
|
|
c13ae25d3d | ||
|
|
c15b83f77e | ||
|
|
0870cdc2a1 | ||
|
|
ac749474bf | ||
|
|
6ad0e4caca | ||
|
|
26753d9c7f | ||
|
|
794f001a53 | ||
|
|
c1c94ea8ef | ||
|
|
4ab22bf1ca | ||
|
|
d352a8546a | ||
|
|
cd8b3abf70 | ||
|
|
874a6934fb | ||
|
|
d4d0b99f45 | ||
|
|
f1a01b4d43 | ||
|
|
56db3cc008 | ||
|
|
1a02168fe6 | ||
|
|
b17641df0d | ||
|
|
eacdd5b74f | ||
|
|
c529f6f273 | ||
|
|
eb971bb601 | ||
|
|
c7a951eacf | ||
|
|
a2ec7886f4 | ||
|
|
0df004df6f | ||
|
|
859281d193 | ||
|
|
c0d0cb5e54 | ||
|
|
56d5faaec5 | ||
|
|
b9bf190c1b | ||
|
|
a2d717b022 | ||
|
|
bad037b598 | ||
|
|
687e84ce86 | ||
|
|
d19e451f85 | ||
|
|
0158ac1040 | ||
|
|
31c6f415d0 | ||
|
|
a4d31d9ac9 | ||
|
|
431d0e7689 | ||
|
|
4177409b59 | ||
|
|
df7d00ad99 | ||
|
|
aaa025c2bb | ||
|
|
69023d9662 | ||
|
|
d5f09736f8 | ||
|
|
fe525a9397 | ||
|
|
cb73c9e14f | ||
|
|
2681c111e5 | ||
|
|
1339c9d61d | ||
|
|
d0c6f36df9 | ||
|
|
776e74a8ad | ||
|
|
f94d231316 | ||
|
|
ce786d2b63 | ||
|
|
88e9ccdfa6 | ||
|
|
674545de8c | ||
|
|
7b1bd7f98a | ||
|
|
2c995ae29c | ||
|
|
10b4f5baca | ||
|
|
bb1e7ef170 | ||
|
|
a232d5e935 | ||
|
|
e051001ea5 | ||
|
|
5ff55f1a1d | ||
|
|
e26c2931ce | ||
|
|
ea7556de39 | ||
|
|
463470ee95 | ||
|
|
c2b7440bdd | ||
|
|
81c8381a90 | ||
|
|
6016e3c155 | ||
|
|
fcc84c35b1 | ||
|
|
82c8be085b | ||
|
|
96dbcf564c | ||
|
|
a230fcaa43 | ||
|
|
a3a15573f9 | ||
|
|
bac4cb9000 | ||
|
|
45b1b397a6 | ||
|
|
b6f5f8b253 | ||
|
|
dcf3d1907a | ||
|
|
acfa3d3b75 | ||
|
|
9254a5ab10 | ||
|
|
c4db283c63 | ||
|
|
387e5e1095 | ||
|
|
83a09ebb94 | ||
|
|
d01e5f5ee2 | ||
|
|
9264d74d51 | ||
|
|
4d0443b337 | ||
|
|
1aa6cb4f35 | ||
|
|
bbe786e063 | ||
|
|
882755fb56 | ||
|
|
8806c7caed | ||
|
|
14942f9653 | ||
|
|
b18b11c682 | ||
|
|
e0a7e115e4 | ||
|
|
26d0ca567f | ||
|
|
c186df94b8 | ||
|
|
d5c092e08d | ||
|
|
49b95df59d | ||
|
|
d60f8bd89b | ||
|
|
4ca910ec98 |
34
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
name: Bug report
|
name: Bug report
|
||||||
about: Create a report to help us improve
|
about: Create a report to help us improve
|
||||||
title: ''
|
title: '[BUG] '
|
||||||
labels: ''
|
labels: 'bug'
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -23,9 +23,35 @@ A clear and concise description of what you expected to happen.
|
|||||||
**Screenshots**
|
**Screenshots**
|
||||||
If applicable, add screenshots to help explain your problem.
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Server Logs**
|
||||||
|
If applicable, add logs to help explain your problem.
|
||||||
|
|
||||||
|
**Browser Logs**
|
||||||
|
If applicable, add logs to help explain your problem.
|
||||||
|
|
||||||
**Version**
|
**Version**
|
||||||
Frontend vX.X
|
vX.X
|
||||||
Frontend vX.X
|
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context about the problem here.
|
Add any other context about the problem here.
|
||||||
|
|
||||||
|
|
||||||
|
**docker-compose.yaml**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
|
||||||
|
YOUR DOCKER-COMPOSE HERE
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
**config.toml**
|
||||||
|
|
||||||
|
```toml
|
||||||
|
|
||||||
|
YOUR CONFIG HERE
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] I understand, that without logs and/or screenshots and a detailed description of the problem, it is very hard to fix bugs.
|
||||||
|
- [ ] I have checked the [documentation](https://maxdorninger.github.io/MediaManager/) for help.
|
||||||
|
- [ ] I have searched the [issues](https://github.com/maxdorninger/MediaManager/issues) for similar issues and found none.
|
||||||
|
|||||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
25
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
open-pull-requests-limit: 5
|
||||||
|
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/web"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
open-pull-requests-limit: 5
|
||||||
|
|
||||||
|
- package-ecosystem: "uv"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
open-pull-requests-limit: 5
|
||||||
|
|
||||||
107
.github/workflows/build-docs.yml
vendored
@@ -1,107 +0,0 @@
|
|||||||
name: Build documentation
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "master" ]
|
|
||||||
paths:
|
|
||||||
- 'Writerside/**'
|
|
||||||
- '.github/workflows/build-docs.yml'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
id-token: write
|
|
||||||
pages: write
|
|
||||||
|
|
||||||
env:
|
|
||||||
INSTANCE: 'Writerside/mm'
|
|
||||||
DOCKER_VERSION: '2025.04.8412'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
algolia_artifact: ${{ steps.define-ids.outputs.algolia_artifact }}
|
|
||||||
artifact: ${{ steps.define-ids.outputs.artifact }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Define instance id and artifacts
|
|
||||||
id: define-ids
|
|
||||||
run: |
|
|
||||||
INSTANCE=${INSTANCE#*/}
|
|
||||||
INSTANCE_ID_UPPER=$(echo "$INSTANCE" | tr '[:lower:]' '[:upper:]')
|
|
||||||
ARTIFACT="webHelp${INSTANCE_ID_UPPER}2-all.zip"
|
|
||||||
ALGOLIA_ARTIFACT="algolia-indexes-${INSTANCE_ID_UPPER}.zip"
|
|
||||||
|
|
||||||
# Print the values
|
|
||||||
echo "INSTANCE_ID_UPPER: $INSTANCE_ID_UPPER"
|
|
||||||
echo "ARTIFACT: $ARTIFACT"
|
|
||||||
echo "ALGOLIA_ARTIFACT: $ALGOLIA_ARTIFACT"
|
|
||||||
|
|
||||||
# Set the environment variables and outputs
|
|
||||||
echo "INSTANCE_ID_UPPER=$INSTANCE_ID_UPPER" >> $GITHUB_ENV
|
|
||||||
echo "ARTIFACT=$ARTIFACT" >> $GITHUB_ENV
|
|
||||||
echo "ALGOLIA_ARTIFACT=$ALGOLIA_ARTIFACT" >> $GITHUB_ENV
|
|
||||||
echo "artifact=$ARTIFACT" >> $GITHUB_OUTPUT
|
|
||||||
echo "algolia_artifact=$ALGOLIA_ARTIFACT" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Build docs using Writerside Docker builder
|
|
||||||
uses: JetBrains/writerside-github-action@v4
|
|
||||||
with:
|
|
||||||
instance: ${{ env.INSTANCE }}
|
|
||||||
docker-version: ${{ env.DOCKER_VERSION }}
|
|
||||||
|
|
||||||
- name: Save artifact with build results
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: docs
|
|
||||||
path: |
|
|
||||||
artifacts/${{ steps.define-ids.outputs.artifact }}
|
|
||||||
artifacts/report.json
|
|
||||||
artifacts/${{ steps.define-ids.outputs.algolia_artifact }}
|
|
||||||
retention-days: 7
|
|
||||||
test:
|
|
||||||
needs: build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Download artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: docs
|
|
||||||
path: artifacts
|
|
||||||
|
|
||||||
- name: Test documentation
|
|
||||||
uses: JetBrains/writerside-checker-action@v1
|
|
||||||
with:
|
|
||||||
instance: ${{ env.INSTANCE }}
|
|
||||||
deploy:
|
|
||||||
environment:
|
|
||||||
name: github-pages
|
|
||||||
url: ${{ steps.deployment.outputs.page_url }}
|
|
||||||
needs: [ build, test ]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Download artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: docs
|
|
||||||
path: artifacts
|
|
||||||
|
|
||||||
- name: Unzip artifact
|
|
||||||
run: unzip -O UTF-8 -qq "artifacts/${{ needs.build.outputs.artifact }}" -d dir
|
|
||||||
|
|
||||||
- name: Setup Pages
|
|
||||||
uses: actions/configure-pages@v4
|
|
||||||
|
|
||||||
- name: Package and upload Pages artifact
|
|
||||||
uses: actions/upload-pages-artifact@v3
|
|
||||||
with:
|
|
||||||
path: dir
|
|
||||||
|
|
||||||
- name: Deploy to GitHub Pages
|
|
||||||
id: deployment
|
|
||||||
uses: actions/deploy-pages@v4
|
|
||||||
211
.github/workflows/build-push-backend.yml
vendored
@@ -4,8 +4,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
tags:
|
|
||||||
- 'v*.*.*'
|
|
||||||
paths:
|
paths:
|
||||||
- 'media_manager/**'
|
- 'media_manager/**'
|
||||||
- 'alembic/**'
|
- 'alembic/**'
|
||||||
@@ -15,6 +13,8 @@ on:
|
|||||||
- 'uv.lock'
|
- 'uv.lock'
|
||||||
- '.github/workflows/build-push-backend.yml'
|
- '.github/workflows/build-push-backend.yml'
|
||||||
- 'tests/**'
|
- 'tests/**'
|
||||||
|
- 'web/**'
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- 'media_manager/**'
|
- 'media_manager/**'
|
||||||
@@ -25,50 +25,150 @@ on:
|
|||||||
- 'uv.lock'
|
- 'uv.lock'
|
||||||
- '.github/workflows/build-push-backend.yml'
|
- '.github/workflows/build-push-backend.yml'
|
||||||
- 'tests/**'
|
- 'tests/**'
|
||||||
|
- 'web/**'
|
||||||
|
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint-code:
|
lint-backend:
|
||||||
name: Lint Python Code
|
name: Lint Python Code
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: astral-sh/ruff-action@v3
|
- uses: astral-sh/ruff-action@v3
|
||||||
with:
|
with:
|
||||||
src: "./media_manager"
|
src: "./media_manager"
|
||||||
|
|
||||||
run-tests:
|
lint-frontend:
|
||||||
needs: lint-code
|
name: Lint Frontend
|
||||||
name: Run Python Tests
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup Node.js
|
||||||
- name: Install uv
|
uses: actions/setup-node@v4
|
||||||
uses: astral-sh/setup-uv@v5
|
with:
|
||||||
|
node-version: '24'
|
||||||
- name: Install the project
|
cache: 'npm'
|
||||||
run: uv sync --locked --all-extras --dev
|
cache-dependency-path: './web/package-lock.json'
|
||||||
|
- name: Install dependencies
|
||||||
- name: Run tests
|
run: npm ci
|
||||||
run: uv run pytest tests
|
working-directory: ./web
|
||||||
|
- name: Lint code
|
||||||
|
run: npm run lint
|
||||||
|
working-directory: ./web
|
||||||
|
|
||||||
build-and-push:
|
build-and-push:
|
||||||
needs: run-tests
|
needs: [lint-frontend, lint-backend]
|
||||||
runs-on: ubuntu-latest
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- arch: amd64
|
||||||
|
runner: ubuntu-24.04
|
||||||
|
- arch: arm64
|
||||||
|
runner: ubuntu-24.04-arm
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
env:
|
||||||
|
QUAY_ORG: ${{ secrets.QUAY_ORG || github.repository_owner }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set repository name to lowercase
|
||||||
uses: docker/setup-qemu-action@v3
|
id: repo_name
|
||||||
|
run: echo "name=$(echo '${{ github.event.repository.name }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
with:
|
||||||
|
install: true
|
||||||
|
driver-opts: image=moby/buildkit:rootless
|
||||||
|
|
||||||
|
- name: Log in to GitHub Container Registry
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Log in to Quay
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: quay.io
|
||||||
|
username: ${{ secrets.QUAY_USERNAME }}
|
||||||
|
password: ${{ secrets.QUAY_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Extract version
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event_name }}" == "release" ]]; then
|
||||||
|
VERSION="${{ github.event.release.tag_name }}"
|
||||||
|
elif [[ "${{ github.ref_type }}" == "tag" ]]; then
|
||||||
|
VERSION="${{ github.ref_name }}"
|
||||||
|
else
|
||||||
|
DATE_STAMP=$(date -u +'%Y.%m.%d')
|
||||||
|
VERSION="dev-${DATE_STAMP}-${{ github.run_number }}"
|
||||||
|
fi
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
VERSION_NO_V=${VERSION#v}
|
||||||
|
echo "version_no_v=$VERSION_NO_V" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Extract metadata (tags + labels)
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
ghcr.io/${{ github.repository_owner }}/${{ steps.repo_name.outputs.name }}/mediamanager
|
||||||
|
quay.io/${{ env.QUAY_ORG }}/mediamanager
|
||||||
|
tags: |
|
||||||
|
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
||||||
|
type=raw,value=${{ steps.version.outputs.version }},enable=${{ github.event_name != 'release' }}
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=pr
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
type=semver,pattern={{major}}
|
||||||
|
type=sha
|
||||||
|
flavor: |
|
||||||
|
suffix=-${{ matrix.arch }},onlatest=true
|
||||||
|
|
||||||
|
- name: Build and push arch image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
platforms: linux/${{ matrix.arch }}
|
||||||
|
push: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
build-args: |
|
||||||
|
VERSION=${{ steps.version.outputs.version }}
|
||||||
|
BASE_PATH=
|
||||||
|
|
||||||
|
publish-manifests:
|
||||||
|
needs: build-and-push
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
env:
|
||||||
|
QUAY_ORG: ${{ secrets.QUAY_ORG }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set repository name to lowercase
|
||||||
|
id: repo_name
|
||||||
|
run: echo "name=$(echo '${{ github.event.repository.name }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
@@ -80,14 +180,38 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Extract metadata (tags, labels) for Docker
|
- name: Log in to Quay
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: quay.io
|
||||||
|
username: ${{ secrets.QUAY_USERNAME }}
|
||||||
|
password: ${{ secrets.QUAY_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Extract version
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event_name }}" == "release" ]]; then
|
||||||
|
VERSION="${{ github.event.release.tag_name }}"
|
||||||
|
elif [[ "${{ github.ref_type }}" == "tag" ]]; then
|
||||||
|
VERSION="${{ github.ref_name }}"
|
||||||
|
else
|
||||||
|
DATE_STAMP=$(date -u +'%Y.%m.%d')
|
||||||
|
VERSION="dev-${DATE_STAMP}-${{ github.run_number }}"
|
||||||
|
fi
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
VERSION_NO_V=${VERSION#v}
|
||||||
|
echo "version_no_v=$VERSION_NO_V" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Extract metadata (tags + labels)
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/backend
|
images: |
|
||||||
|
ghcr.io/${{ github.repository_owner }}/${{ steps.repo_name.outputs.name }}/mediamanager
|
||||||
|
quay.io/${{ env.QUAY_ORG }}/mediamanager
|
||||||
tags: |
|
tags: |
|
||||||
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') && github.event_name != 'pull_request' }}
|
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
||||||
type=ref,event=tag
|
type=raw,value=${{ steps.version.outputs.version }},enable=${{ github.event_name != 'release' }}
|
||||||
type=ref,event=branch
|
type=ref,event=branch
|
||||||
type=ref,event=pr
|
type=ref,event=pr
|
||||||
type=semver,pattern={{version}}
|
type=semver,pattern={{version}}
|
||||||
@@ -95,24 +219,21 @@ jobs:
|
|||||||
type=semver,pattern={{major}}
|
type=semver,pattern={{major}}
|
||||||
type=sha
|
type=sha
|
||||||
|
|
||||||
- name: Extract version
|
- name: Create and push multi-arch manifests
|
||||||
id: version
|
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
|
set -euo pipefail
|
||||||
VERSION=${GITHUB_REF#refs/tags/}
|
mapfile -t TAGS <<< "${{ steps.meta.outputs.tags }}"
|
||||||
else
|
for TAG in "${TAGS[@]}"; do
|
||||||
VERSION="dev-${GITHUB_SHA::7}"
|
docker buildx imagetools create \
|
||||||
fi
|
--tag "${TAG}" \
|
||||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
"${TAG}-amd64" \
|
||||||
|
"${TAG}-arm64"
|
||||||
|
done
|
||||||
|
|
||||||
- name: Build and push Docker image
|
- name: Inspect image (GHCR)
|
||||||
uses: docker/build-push-action@v6
|
run: |
|
||||||
with:
|
docker buildx imagetools inspect ghcr.io/${{ github.repository_owner }}/${{ steps.repo_name.outputs.name }}/mediamanager:${{ steps.version.outputs.version_no_v }}
|
||||||
context: .
|
|
||||||
file: ./Dockerfile
|
- name: Inspect image (Quay)
|
||||||
platforms: linux/amd64,linux/arm64
|
run: |
|
||||||
push: true
|
docker buildx imagetools inspect quay.io/${{ env.QUAY_ORG }}/mediamanager:${{ steps.version.outputs.version_no_v }}
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
build-args: |
|
|
||||||
VERSION=${{ steps.version.outputs.version }}
|
|
||||||
98
.github/workflows/build-push-frontend.yml
vendored
@@ -1,98 +0,0 @@
|
|||||||
name: Build and Push Frontend Docker Image
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
tags:
|
|
||||||
- 'v*.*.*'
|
|
||||||
paths:
|
|
||||||
- 'web/**'
|
|
||||||
- '.github/workflows/build-push-frontend.yml'
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- 'web/**'
|
|
||||||
- '.github/workflows/build-push-frontend.yml'
|
|
||||||
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
lint:
|
|
||||||
name: Lint Frontend
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '20'
|
|
||||||
cache: 'npm'
|
|
||||||
cache-dependency-path: './web/package-lock.json'
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm ci
|
|
||||||
working-directory: ./web
|
|
||||||
- name: Lint code
|
|
||||||
run: npm run lint
|
|
||||||
working-directory: ./web
|
|
||||||
build-and-push:
|
|
||||||
needs: lint
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Log in to GitHub Container Registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Extract metadata (tags, labels) for Docker
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/frontend
|
|
||||||
tags: |
|
|
||||||
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') && github.event_name != 'pull_request' }}
|
|
||||||
type=ref,event=tag
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=pr
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=semver,pattern={{major}}
|
|
||||||
type=sha
|
|
||||||
|
|
||||||
- name: Extract version
|
|
||||||
id: version
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
|
|
||||||
VERSION=${GITHUB_REF#refs/tags/}
|
|
||||||
else
|
|
||||||
VERSION="dev-${GITHUB_SHA::7}"
|
|
||||||
fi
|
|
||||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Build and push Docker image
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
context: ./web
|
|
||||||
file: ./web/Dockerfile
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
build-args: |
|
|
||||||
VERSION=${{ steps.version.outputs.version }}
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
173
.github/workflows/build-push-metadata_relay.yml
vendored
@@ -4,22 +4,21 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
tags:
|
|
||||||
- 'v*.*.*'
|
|
||||||
paths:
|
paths:
|
||||||
- 'metadata_relay/**'
|
- 'metadata_relay/**'
|
||||||
- '.github/workflows/python-lint_metadata_relay.yaml'
|
- '.github/workflows/build-push-metadata_relay.yml'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- 'metadata_relay/**'
|
- 'metadata_relay/**'
|
||||||
- '.github/workflows/python-lint_metadata_relay.yaml'
|
- '.github/workflows/build-push-metadata_relay.yml'
|
||||||
|
release:
|
||||||
|
types: [ published ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint-code:
|
lint-code:
|
||||||
name: Lint Python Code
|
name: Lint Python Code
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: astral-sh/ruff-action@v3
|
- uses: astral-sh/ruff-action@v3
|
||||||
@@ -28,17 +27,112 @@ jobs:
|
|||||||
|
|
||||||
build-and-push:
|
build-and-push:
|
||||||
needs: lint-code
|
needs: lint-code
|
||||||
runs-on: ubuntu-latest
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- arch: amd64
|
||||||
|
runner: ubuntu-24.04
|
||||||
|
- arch: arm64
|
||||||
|
runner: ubuntu-24.04-arm
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
env:
|
||||||
|
QUAY_ORG: ${{ secrets.QUAY_ORG || github.repository_owner }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set repository name to lowercase
|
||||||
uses: docker/setup-qemu-action@v3
|
id: repo_name
|
||||||
|
run: echo "name=$(echo '${{ github.event.repository.name }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
with:
|
||||||
|
install: true
|
||||||
|
driver-opts: image=moby/buildkit:rootless
|
||||||
|
|
||||||
|
- name: Log in to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Log in to Quay
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||||
|
with:
|
||||||
|
registry: quay.io
|
||||||
|
username: ${{ secrets.QUAY_USERNAME }}
|
||||||
|
password: ${{ secrets.QUAY_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Extract version
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event_name }}" == "release" ]]; then
|
||||||
|
VERSION="${{ github.event.release.tag_name }}"
|
||||||
|
elif [[ "${{ github.ref_type }}" == "tag" ]]; then
|
||||||
|
VERSION="${{ github.ref_name }}"
|
||||||
|
else
|
||||||
|
DATE_STAMP=$(date -u +'%Y.%m.%d')
|
||||||
|
VERSION="dev-${DATE_STAMP}-${{ github.run_number }}"
|
||||||
|
fi
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
VERSION_NO_V=${VERSION#v}
|
||||||
|
echo "version_no_v=$VERSION_NO_V" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Extract metadata (tags + labels)
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
ghcr.io/${{ github.repository_owner }}/${{ steps.repo_name.outputs.name }}/metadata_relay
|
||||||
|
quay.io/${{ env.QUAY_ORG }}/metadata_relay
|
||||||
|
tags: |
|
||||||
|
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
||||||
|
type=raw,value=${{ steps.version.outputs.version }},enable=${{ github.event_name != 'release' }}
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=pr
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
type=semver,pattern={{major}}
|
||||||
|
type=sha
|
||||||
|
flavor: |
|
||||||
|
suffix=-${{ matrix.arch }},onlatest=true
|
||||||
|
|
||||||
|
- name: Build and push arch image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: ./metadata_relay
|
||||||
|
file: ./metadata_relay/Dockerfile
|
||||||
|
platforms: linux/${{ matrix.arch }}
|
||||||
|
push: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
build-args: |
|
||||||
|
VERSION=${{ steps.version.outputs.version }}
|
||||||
|
|
||||||
|
publish-manifests:
|
||||||
|
needs: build-and-push
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
env:
|
||||||
|
QUAY_ORG: ${{ secrets.QUAY_ORG || github.repository_owner }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set repository name to lowercase
|
||||||
|
id: repo_name
|
||||||
|
run: echo "name=$(echo '${{ github.event.repository.name }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
@@ -50,14 +144,38 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Extract metadata (tags, labels) for Docker
|
- name: Log in to Quay
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: quay.io
|
||||||
|
username: ${{ secrets.QUAY_USERNAME }}
|
||||||
|
password: ${{ secrets.QUAY_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Extract version
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event_name }}" == "release" ]]; then
|
||||||
|
VERSION="${{ github.event.release.tag_name }}"
|
||||||
|
elif [[ "${{ github.ref_type }}" == "tag" ]]; then
|
||||||
|
VERSION="${{ github.ref_name }}"
|
||||||
|
else
|
||||||
|
DATE_STAMP=$(date -u +'%Y.%m.%d')
|
||||||
|
VERSION="dev-${DATE_STAMP}-${{ github.run_number }}"
|
||||||
|
fi
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
VERSION_NO_V=${VERSION#v}
|
||||||
|
echo "version_no_v=$VERSION_NO_V" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Extract metadata (tags + labels)
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/metadata_relay
|
images: |
|
||||||
|
ghcr.io/${{ github.repository_owner }}/${{ steps.repo_name.outputs.name }}/metadata_relay
|
||||||
|
quay.io/${{ env.QUAY_ORG }}/metadata_relay
|
||||||
tags: |
|
tags: |
|
||||||
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') && github.event_name != 'pull_request' }}
|
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
||||||
type=ref,event=tag
|
type=raw,value=${{ steps.version.outputs.version }},enable=${{ github.event_name != 'release' }}
|
||||||
type=ref,event=branch
|
type=ref,event=branch
|
||||||
type=ref,event=pr
|
type=ref,event=pr
|
||||||
type=semver,pattern={{version}}
|
type=semver,pattern={{version}}
|
||||||
@@ -65,14 +183,21 @@ jobs:
|
|||||||
type=semver,pattern={{major}}
|
type=semver,pattern={{major}}
|
||||||
type=sha
|
type=sha
|
||||||
|
|
||||||
- name: Build and push Docker image
|
- name: Create and push multi-arch manifests
|
||||||
uses: docker/build-push-action@v6
|
run: |
|
||||||
with:
|
set -euo pipefail
|
||||||
context: ./metadata_relay
|
mapfile -t TAGS <<< "${{ steps.meta.outputs.tags }}"
|
||||||
file: ./metadata_relay/Dockerfile
|
for TAG in "${TAGS[@]}"; do
|
||||||
platforms: linux/amd64,linux/arm64
|
docker buildx imagetools create \
|
||||||
push: true
|
--tag "${TAG}" \
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
"${TAG}-amd64" \
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
"${TAG}-arm64"
|
||||||
build-args: |
|
done
|
||||||
VERSION=${{ steps.meta.outputs.version }}
|
|
||||||
|
- name: Inspect image (GHCR)
|
||||||
|
run: |
|
||||||
|
docker buildx imagetools inspect ghcr.io/${{ github.repository_owner }}/${{ steps.repo_name.outputs.name }}/metadata_relay:${{ steps.version.outputs.version_no_v }}
|
||||||
|
|
||||||
|
- name: Inspect image (Quay)
|
||||||
|
run: |
|
||||||
|
docker buildx imagetools inspect quay.io/${{ env.QUAY_ORG }}/metadata_relay:${{ steps.version.outputs.version_no_v }}
|
||||||
|
|||||||
62
.github/workflows/docs.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
name: Publish docs via GitHub Pages
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
set_default_alias:
|
||||||
|
description: 'Alias to set as default (e.g. latest, master)'
|
||||||
|
required: false
|
||||||
|
default: 'latest'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Configure Git Credentials
|
||||||
|
run: |
|
||||||
|
git config user.name github-actions[bot]
|
||||||
|
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
|
||||||
|
|
||||||
|
- uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
|
||||||
|
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
key: mkdocs-material-${{ env.cache_id }}
|
||||||
|
path: .cache
|
||||||
|
restore-keys: |
|
||||||
|
mkdocs-material-
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pip install mkdocs-material mike
|
||||||
|
|
||||||
|
- name: Deploy (master)
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
run: |
|
||||||
|
mike deploy --push --update-aliases master
|
||||||
|
|
||||||
|
- name: Deploy (tag)
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
run: |
|
||||||
|
version=${GITHUB_REF#refs/tags/}
|
||||||
|
mike deploy --push --update-aliases $version latest --title "$version"
|
||||||
|
mike set-default --push latest
|
||||||
|
|
||||||
|
- name: Set Default (Manual)
|
||||||
|
if: github.event_name == 'workflow_dispatch' && github.event.inputs.set_default_alias != ''
|
||||||
|
run: |
|
||||||
|
mike set-default --push ${{ github.event.inputs.set_default_alias }}
|
||||||
29
.github/workflows/python-lint.yaml
vendored
@@ -1,29 +0,0 @@
|
|||||||
name: Ruff
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
paths:
|
|
||||||
- 'media_manager/**'
|
|
||||||
- 'alembic/**'
|
|
||||||
- 'pyproject.toml'
|
|
||||||
- 'uv.lock'
|
|
||||||
- '.github/workflows/python-lint.yaml'
|
|
||||||
- 'tests/**'
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- 'media_manager/**'
|
|
||||||
- 'alembic/**'
|
|
||||||
- 'pyproject.toml'
|
|
||||||
- 'uv.lock'
|
|
||||||
- '.github/workflows/python-lint.yaml'
|
|
||||||
- 'tests/**'
|
|
||||||
workflow_dispatch:
|
|
||||||
jobs:
|
|
||||||
ruff:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: astral-sh/ruff-action@v3
|
|
||||||
with:
|
|
||||||
src: "./media_manager"
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
name: Ruff Metadata Relay
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
paths:
|
|
||||||
- 'metadata_relay/**'
|
|
||||||
- '.github/workflows/python-lint_metadata_relay.yaml'
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- 'metadata_relay/**'
|
|
||||||
- '.github/workflows/python-lint_metadata_relay.yaml'
|
|
||||||
workflow_dispatch:
|
|
||||||
jobs:
|
|
||||||
ruff:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: astral-sh/ruff-action@v3
|
|
||||||
with:
|
|
||||||
src: "./metadata_relay"
|
|
||||||
31
.github/workflows/python-tests.yml
vendored
@@ -1,31 +0,0 @@
|
|||||||
name: Run Python Tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
paths:
|
|
||||||
- 'media_manager/**'
|
|
||||||
- 'alembic/**'
|
|
||||||
- 'alembic.ini'
|
|
||||||
- 'pyproject.toml'
|
|
||||||
- 'uv.lock'
|
|
||||||
- '.github/workflows/python-tests.yml'
|
|
||||||
- 'tests/**'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
run-tests:
|
|
||||||
name: Run Python Tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install uv
|
|
||||||
uses: astral-sh/setup-uv@v5
|
|
||||||
|
|
||||||
- name: Install the project
|
|
||||||
run: uv sync --locked --all-extras --dev
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: uv run pytest tests
|
|
||||||
31
.github/workflows/web-lint.yml
vendored
@@ -1,31 +0,0 @@
|
|||||||
name: Lint Frontend
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- 'web/**'
|
|
||||||
- '.github/workflows/web-lint.yml'
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
paths:
|
|
||||||
- 'web/**'
|
|
||||||
- '.github/workflows/web-lint.yml'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
lint:
|
|
||||||
name: Lint Frontend
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '20'
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm install
|
|
||||||
working-directory: ./web
|
|
||||||
- name: Lint code
|
|
||||||
run: npm run lint
|
|
||||||
working-directory: ./web
|
|
||||||
|
|
||||||
8
.gitignore
vendored
@@ -11,6 +11,11 @@ res/*
|
|||||||
media_manager/indexer/indexers/prowlarr.http
|
media_manager/indexer/indexers/prowlarr.http
|
||||||
*.egg-info
|
*.egg-info
|
||||||
.env
|
.env
|
||||||
|
config
|
||||||
|
images
|
||||||
|
data
|
||||||
|
res
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
web/cache/
|
web/cache/
|
||||||
@@ -43,3 +48,6 @@ __pycache__
|
|||||||
|
|
||||||
# Postgres
|
# Postgres
|
||||||
/postgres
|
/postgres
|
||||||
|
|
||||||
|
# MkDocs
|
||||||
|
site/
|
||||||
|
|||||||
128
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity
|
||||||
|
and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the
|
||||||
|
overall community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or
|
||||||
|
advances of any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email
|
||||||
|
address, without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
97409287+maxdorninger@users.noreply.github.com.
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series
|
||||||
|
of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or
|
||||||
|
permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
the community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
|
https://www.contributor-covenant.org/translations.
|
||||||
33
CONTRIBUTING.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Contributing to MediaManager
|
||||||
|
|
||||||
|
First off, thank you for considering contributing to MediaManager.
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
Following this guide helps me merge your PRs faster, prevents unnecessary back-and-forth and wasted effort.
|
||||||
|
|
||||||
|
## How to suggest a feature or enhancement
|
||||||
|
|
||||||
|
Open an issue which describes the feature you would like to
|
||||||
|
see, why you need it, and how it should work.
|
||||||
|
There we can discuss its scope and implementation.
|
||||||
|
|
||||||
|
## How to contribute
|
||||||
|
|
||||||
|
Generally, if you have any questions or need help on the implementation side of MediaManager,
|
||||||
|
just ask in the issue, or in a draft PR.
|
||||||
|
|
||||||
|
Also, see the contribution guide in the docs for information on how to setup the dev environment:
|
||||||
|
https://maxdorninger.github.io/MediaManager/
|
||||||
|
|
||||||
|
### For something that is a one or two line fix:
|
||||||
|
|
||||||
|
Make the change, and open a PR with a short description of what you changed and why.
|
||||||
|
|
||||||
|
### For something that is bigger than a one or two line fix:
|
||||||
|
|
||||||
|
Explain why you are making the change.
|
||||||
|
Be sure to give a rough overview on how your implementation works, and maybe any design decisions you made.
|
||||||
|
Also include any relevant limitations or trade-offs you made.
|
||||||
|
|
||||||
|
It's best to also open an issue first to discuss larger changes before you start working on them.
|
||||||
80
Dockerfile
@@ -1,29 +1,67 @@
|
|||||||
FROM ghcr.io/astral-sh/uv:debian-slim
|
FROM node:24-alpine AS frontend-build
|
||||||
ARG VERSION
|
WORKDIR /frontend
|
||||||
LABEL version=${VERSION}
|
|
||||||
LABEL description="Docker image for the backend of MediaManager"
|
|
||||||
ENV MISC__IMAGE_DIRECTORY=/data/images \
|
|
||||||
MISC__TV_DIRECTORY=/data/tv \
|
|
||||||
MISC__MOVIE_DIRECTORY=/data/movies \
|
|
||||||
MISC__TORRENT_DIRECTORY=/data/torrents \
|
|
||||||
PUBLIC_VERSION=${VERSION} \
|
|
||||||
MISC__API_BASE_PATH="/api/v1" \
|
|
||||||
CONFIG_FILE="/app/config.toml"
|
|
||||||
|
|
||||||
WORKDIR /app
|
COPY web/package*.json ./
|
||||||
|
RUN npm ci && npm cache clean --force
|
||||||
|
|
||||||
|
COPY web/ ./
|
||||||
|
|
||||||
|
ARG VERSION
|
||||||
|
ARG BASE_PATH=""
|
||||||
|
RUN env PUBLIC_VERSION=${VERSION} PUBLIC_API_URL=${BASE_PATH} BASE_PATH=${BASE_PATH}/web npm run build
|
||||||
|
|
||||||
|
FROM ghcr.io/astral-sh/uv:python3.13-trixie-slim AS base
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y ca-certificates gcc mime-support curl gzip unzip tar 7zip bzip2 unar
|
apt-get install -y ca-certificates bash libtorrent21 gcc bc locales postgresql media-types mailcap curl gzip unzip tar 7zip bzip2 unar gosu && \
|
||||||
|
apt-get clean && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY pyproject.toml uv.lock ./
|
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
|
||||||
|
RUN locale-gen
|
||||||
|
ENV LANG=en_US.UTF-8
|
||||||
|
ENV LC_ALL=en_US.UTF-8
|
||||||
|
|
||||||
RUN uv sync --locked
|
# Create a non-root user and group
|
||||||
|
RUN groupadd -g 1000 mediamanager && \
|
||||||
|
useradd -m -u 1000 -g mediamanager mediamanager
|
||||||
|
|
||||||
COPY --chmod=755 mediamanager-backend-startup.sh .
|
FROM base AS dependencies
|
||||||
COPY media_manager ./media_manager
|
WORKDIR /app
|
||||||
COPY alembic ./alembic
|
# Ensure mediamanager owns /app
|
||||||
COPY alembic.ini .
|
RUN chown -R mediamanager:mediamanager /app
|
||||||
|
|
||||||
HEALTHCHECK CMD curl -f http://localhost:8000${MISC__API_BASE_PATH}/ || exit 1
|
USER mediamanager
|
||||||
|
|
||||||
|
ENV UV_CACHE_DIR=/home/mediamanager/.cache/uv \
|
||||||
|
UV_LINK_MODE=copy
|
||||||
|
|
||||||
|
COPY --chown=mediamanager:mediamanager pyproject.toml uv.lock ./
|
||||||
|
RUN --mount=type=cache,target=/home/mediamanager/.cache/uv,uid=1000,gid=1000 \
|
||||||
|
uv sync --locked
|
||||||
|
|
||||||
|
FROM dependencies AS app
|
||||||
|
ARG VERSION
|
||||||
|
ARG BASE_PATH=""
|
||||||
|
LABEL author="github.com/maxdorninger"
|
||||||
|
LABEL version=${VERSION}
|
||||||
|
LABEL description="Docker image for MediaManager"
|
||||||
|
USER root
|
||||||
|
|
||||||
|
ENV PUBLIC_VERSION=${VERSION} \
|
||||||
|
CONFIG_DIR="/app/config" \
|
||||||
|
BASE_PATH=${BASE_PATH} \
|
||||||
|
FRONTEND_FILES_DIR="/app/web/build"
|
||||||
|
|
||||||
|
COPY --chown=mediamanager:mediamanager --chmod=755 mediamanager-startup.sh .
|
||||||
|
COPY --chown=mediamanager:mediamanager config.example.toml .
|
||||||
|
COPY --chown=mediamanager:mediamanager media_manager ./media_manager
|
||||||
|
COPY --chown=mediamanager:mediamanager alembic ./alembic
|
||||||
|
COPY --chown=mediamanager:mediamanager alembic.ini .
|
||||||
|
|
||||||
|
HEALTHCHECK CMD curl -f http://localhost:8000${BASE_PATH}/api/v1/health || exit 1
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
CMD ["/app/mediamanager-backend-startup.sh"]
|
CMD ["/app/mediamanager-startup.sh"]
|
||||||
|
|
||||||
|
FROM app AS production
|
||||||
|
COPY --chown=mediamanager:mediamanager --from=frontend-build /frontend/build /app/web/build
|
||||||
|
|||||||
4
LICENSE
@@ -629,8 +629,8 @@ to attach them to the start of each source file to most effectively
|
|||||||
state the exclusion of warranty; and each file should have at least
|
state the exclusion of warranty; and each file should have at least
|
||||||
the "copyright" line and a pointer to where the full notice is found.
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
<one line to give the program's name and a brief idea of what it does.>
|
MediaManager, an automatic media downloader and organizer.
|
||||||
Copyright (C) <year> <name of author>
|
Copyright (C) 2025 Maximilian Dorninger
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License as published
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
|||||||
49
Makefile
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
SHELL := /bin/bash
|
||||||
|
|
||||||
|
# Docker Compose command (override if needed)
|
||||||
|
COMPOSE ?= docker compose
|
||||||
|
DC := $(COMPOSE) -f docker-compose.dev.yaml
|
||||||
|
|
||||||
|
# Log args passthrough, e.g.:
|
||||||
|
# make logs ARGS="--follow --tail=100"
|
||||||
|
ARGS ?=
|
||||||
|
|
||||||
|
# Service names (override if your compose uses different names)
|
||||||
|
APP_SVC ?= mediamanager
|
||||||
|
FRONTEND_SVC ?= frontend
|
||||||
|
|
||||||
|
.PHONY: help up down logs ps restartapp frontend
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "Usage:"
|
||||||
|
@echo " All commands run using the dev docker compose file ($(DEV_FILE))"
|
||||||
|
@echo ""
|
||||||
|
@echo " make up # Development environment up, runs with --build flag to rebuild if necessary"
|
||||||
|
@echo " make down # Development environment down"
|
||||||
|
@echo " make logs ARGS=\"...\" # (Optional) Set ARGS like \"--follow --tail=100\""
|
||||||
|
@echo " make ps | restart # Check status or restart containers"
|
||||||
|
@echo " make app # Shell into $(APP_SVC) container"
|
||||||
|
@echo " make frontend # Shell into $(FRONTEND_SVC) container"
|
||||||
|
|
||||||
|
# Core lifecycle
|
||||||
|
up:
|
||||||
|
$(DC) up -d --build
|
||||||
|
|
||||||
|
down:
|
||||||
|
$(DC) down
|
||||||
|
|
||||||
|
logs:
|
||||||
|
$(DC) logs $(ARGS)
|
||||||
|
|
||||||
|
ps:
|
||||||
|
$(DC) ps
|
||||||
|
|
||||||
|
restart:
|
||||||
|
$(DC) restart
|
||||||
|
|
||||||
|
# Interactive shells (prefer bash, fallback to sh)
|
||||||
|
app:
|
||||||
|
@$(DC) exec -it $(APP_SVC) bash 2>/dev/null || $(DC) exec -it $(APP_SVC) sh
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
@$(DC) exec -it $(FRONTEND_SVC) bash 2>/dev/null || $(DC) exec -it $(FRONTEND_SVC) sh
|
||||||
127
README.md
@@ -1,7 +1,7 @@
|
|||||||
<br />
|
<br />
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://maxdorninger.github.io/MediaManager">
|
<a href="https://maxdorninger.github.io/MediaManager/">
|
||||||
<img src="https://raw.githubusercontent.com/maxdorninger/MediaManager/refs/heads/master/Writerside/images/logo.svg" alt="Logo" width="260" height="260">
|
<img src="https://raw.githubusercontent.com/maxdorninger/MediaManager/refs/heads/master/web/static/logo.svg" alt="Logo" width="260" height="260">
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<h3 align="center">MediaManager</h3>
|
<h3 align="center">MediaManager</h3>
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
Modern management system for your media library
|
Modern management system for your media library
|
||||||
<br />
|
<br />
|
||||||
<a href="https://maxdorninger.github.io/MediaManager/introduction.html"><strong>Explore the docs »</strong></a>
|
<a href="https://maxdorninger.github.io/MediaManager/"><strong>Explore the docs »</strong></a>
|
||||||
<br />
|
<br />
|
||||||
<a href="https://github.com/maxdorninger/MediaManager/issues/new?labels=bug&template=bug_report.md">Report Bug</a>
|
<a href="https://github.com/maxdorninger/MediaManager/issues/new?labels=bug&template=bug_report.md">Report Bug</a>
|
||||||
·
|
·
|
||||||
@@ -18,25 +18,24 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
MediaManager is modern software to manage your TV and movie library. It is designed to be a replacement for Sonarr,
|
MediaManager is the modern, easy-to-use successor to the fragmented "Arr" stack. Manage, discover, and automate your TV and movie collection in a single, simple interface.
|
||||||
Radarr, Overseer, and Jellyseer.
|
|
||||||
It supports TVDB and TMDB for metadata, supports OIDC and OAuth 2.0 for authentication and supports Prowlarr and
|
|
||||||
Jackett.
|
|
||||||
MediaManager is built first and foremost for deployment with Docker, making it easy to set up.
|
|
||||||
|
|
||||||
It also provides an API to interact with the software programmatically, allowing for automation and integration with
|
Key features:
|
||||||
other services.
|
- support for OAuth/OIDC
|
||||||
|
- support for TVDB and TMDB
|
||||||
|
- made to be deployed with Docker
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```
|
```sh
|
||||||
wget -O docker-compose.yaml https://raw.githubusercontent.com/maxdorninger/MediaManager/refs/heads/master/docker-compose.yaml
|
wget -O docker-compose.yaml https://github.com/maxdorninger/MediaManager/releases/latest/download/docker-compose.yaml
|
||||||
wget -O docker-compose.yaml https://raw.githubusercontent.com/maxdorninger/MediaManager/refs/heads/master/config.toml
|
mkdir config
|
||||||
# you probably need to edit the config.toml file, for more help see the documentation
|
wget -O ./config/config.toml https://github.com/maxdorninger/MediaManager/releases/latest/download/config.example.toml
|
||||||
docker compose up -d
|
# you probably need to edit the config.toml file in the ./config directory, for more help see the documentation
|
||||||
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
### [View the docs for installation instructions and more](https://maxdorninger.github.io/MediaManager/configuration-overview.html#configuration-overview)
|
### [View the docs for installation instructions and more](https://maxdorninger.github.io/MediaManager/)
|
||||||
|
|
||||||
## Support MediaManager
|
## Support MediaManager
|
||||||
|
|
||||||
@@ -48,94 +47,56 @@ other services.
|
|||||||
<img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" >
|
<img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" >
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
## Check out the awesome sponsors of MediaManager ❤️
|
## Check out the awesome sponsors of MediaManager ❤️
|
||||||
|
|
||||||
<a href="https://fosstodon.org/@aljazmerzen"><img src="https://github.com/aljazerzen.png" width="80px" alt="Aljaž Mur Eržen" /></a>
|
<a href="https://fosstodon.org/@aljazmerzen"><img src="https://github.com/aljazerzen.png" width="80px" alt="Aljaž Mur Eržen" /></a>
|
||||||
<a href="https://github.com/ldrrp"><img src="https://github.com/ldrrp.png" width="80px" alt="Luis Rodriguez" /></a>
|
<a href="https://github.com/ldrrp"><img src="https://github.com/ldrrp.png" width="80px" alt="Luis Rodriguez" /></a>
|
||||||
|
<a href="https://github.com/brandon-dacrib"><img src="https://github.com/brandon-dacrib.png" width="80px" alt="Brandon P." /></a>
|
||||||
|
<a href="https://github.com/SeimusS"><img src="https://github.com/SeimusS.png" width="80px" alt="SeimusS" /></a>
|
||||||
|
<a href="https://github.com/HadrienKerlero"><img src="https://github.com/HadrienKerlero.png" width="80px" alt="HadrienKerlero" /></a>
|
||||||
|
<a href="https://github.com/keyxmakerx"><img src="https://github.com/keyxmakerx.png" width="80px" alt="keyxmakerx" /></a>
|
||||||
|
<a href="https://github.com/LITUATUI"><img src="https://github.com/LITUATUI.png" width="80px" alt="LITUATUI" /></a>
|
||||||
|
<a href="https://buymeacoffee.com/maxdorninger"><img src="https://cdn.buymeacoffee.com/uploads/profile_pictures/default/v2/B6CDBD/NI.png" width="80px" alt="Nicolas" /></a>
|
||||||
|
<a href="https://buymeacoffee.com/maxdorninger"><img src="https://cdn.buymeacoffee.com/uploads/profile_pictures/default/v2/DEBBB9/JO.png" width="80px" alt="Josh" /></a>
|
||||||
|
<a href="https://buymeacoffee.com/maxdorninger"><img src="https://cdn.buymeacoffee.com/uploads/profile_pictures/2025/11/2VeQ8sTGPhj4tiLy.jpg" width="80px" alt="PuppiestDoggo" /></a>
|
||||||
|
<a href="https://github.com/seferino-fernandez"><img src="https://avatars.githubusercontent.com/u/5546622" width="80px" alt="Seferino" /></a>
|
||||||
|
<a href="https://buymeacoffee.com/maxdorninger"><img src="https://cdn.buymeacoffee.com/uploads/profile_pictures/default/v2/EC9689/SY.png" width="80px" alt="syn" /></a>
|
||||||
|
|
||||||
|
## Star History
|
||||||
|
|
||||||
<!-- ROADMAP -->
|
[](https://www.star-history.com/#maxdorninger/MediaManager&Date)
|
||||||
## Roadmap
|
|
||||||
|
|
||||||
- [x] support for more torrent indexers
|
|
||||||
- [x] fully automatic downloads
|
|
||||||
- [x] add tests
|
|
||||||
- [x] add more logs/errors
|
|
||||||
- [x] make API return proper error codes
|
|
||||||
- [x] optimize images for web in the backend
|
|
||||||
- [x] responsive ui
|
|
||||||
- [x] automatically update metadata of shows
|
|
||||||
- [x] automatically download new seasons/episodes of shows
|
|
||||||
- [x] add fallback to just copy files if hardlinks don't work
|
|
||||||
- [x] add check at startup if hardlinks work
|
|
||||||
- [x] create separate metadata relay service, so that api keys for TMDB and TVDB are not strictly needed
|
|
||||||
- [x] support for movies
|
|
||||||
- [x] expand README with more information and a quickstart guide
|
|
||||||
- [x] improve reliability of scheduled tasks
|
|
||||||
- [x] add notification system
|
|
||||||
- [x] add sequence diagrams to the documentation
|
|
||||||
- [x] add usenet support
|
|
||||||
- [ ] provide example configuration files
|
|
||||||
- [ ] make media sorting algorithm configurable
|
|
||||||
- [ ] add in-depth documentation on the architecture of the codebase
|
|
||||||
- [ ] make indexer module multithreaded
|
|
||||||
- [ ] add support for deluge and transmission
|
|
||||||
- [ ] add delete button for movies/TV shows
|
|
||||||
- [ ] rework prowlarr module (select which indexers to use, etc.)
|
|
||||||
- [ ] _maybe_ rework the logo
|
|
||||||
- [ ] _maybe_ add support for configuration via toml/yaml config file
|
|
||||||
|
|
||||||
See the [open issues](hhttps://maxdorninger.github.io/MediaManager/issues) for a full list of proposed features (and known issues).
|
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||

|
<img width="1902" height="887" alt="Screenshot 2025-12-08 at 00 44 26" src="https://github.com/user-attachments/assets/4c3fbc73-6358-44ac-a948-0eb35ab116fd" />
|
||||||

|
<img width="1875" height="882" alt="Screenshot 2025-12-08 at 00 46 39" src="https://github.com/user-attachments/assets/75645cb6-236b-4bef-9a21-76bfd6d530f9" />
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
<img width="1883" height="890" alt="Screenshot 2025-12-08 at 00 48 23" src="https://github.com/user-attachments/assets/c5789559-5aa7-4c26-9442-a2b156e99b1c" />
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Developer Quick Start
|
## Developer Quick Start
|
||||||
|
|
||||||
```bash
|
For the developer guide see the [Developer Guide](https://maxdorninger.github.io/MediaManager/).
|
||||||
pip install uv
|
|
||||||
uv venv
|
|
||||||
# Activate the virtual environment
|
|
||||||
uv pip install -e .
|
|
||||||
```
|
|
||||||
```bash
|
|
||||||
docker compose up db -d
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uv run alembic upgrade head
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get the frontend up and running
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /web && npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
### Now start the backend and frontend
|
|
||||||
```bash
|
|
||||||
fastapi dev /media_manager/main.py --reload --host
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /web && npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
<!-- LICENSE -->
|
<!-- LICENSE -->
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Distributed under the AGPL 3.0. See `LICENSE.txt` for more information.
|
Distributed under the AGPL 3.0. See `LICENSE.txt` for more information.
|
||||||
|
|
||||||
|
|
||||||
<!-- ACKNOWLEDGMENTS -->
|
<!-- ACKNOWLEDGMENTS -->
|
||||||
|
|
||||||
## Acknowledgments
|
## Acknowledgments
|
||||||
|
|
||||||
|
Thanks to DigitalOcean for sponsoring the project!
|
||||||
|
|
||||||
|
[](https://www.digitalocean.com/?refcode=4edf05429dca&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge)
|
||||||
|
|
||||||
* [Thanks to Pawel Czerwinski for the image on the login screen](https://unsplash.com/@pawel_czerwinski)
|
* [Thanks to Pawel Czerwinski for the image on the login screen](https://unsplash.com/@pawel_czerwinski)
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE instance-profile
|
|
||||||
SYSTEM "https://resources.jetbrains.com/writerside/1.0/product-profile.dtd">
|
|
||||||
|
|
||||||
<instance-profile id="Writerside_libraries"
|
|
||||||
name="Writerside_libraries" is-library="true" start-page="notes.topic">
|
|
||||||
|
|
||||||
<toc-element topic="notes.topic"/>
|
|
||||||
</instance-profile>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE categories
|
|
||||||
SYSTEM "https://resources.jetbrains.com/writerside/1.0/categories.dtd">
|
|
||||||
<categories>
|
|
||||||
<category id="wrs" name="Writerside documentation" order="1"/>
|
|
||||||
</categories>
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE buildprofiles SYSTEM "https://resources.jetbrains.com/writerside/1.0/build-profiles.dtd">
|
|
||||||
<buildprofiles xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:noNamespaceSchemaLocation="https://resources.jetbrains.com/writerside/1.0/build-profiles.xsd">
|
|
||||||
|
|
||||||
<variables></variables>
|
|
||||||
<build-profile instance="mm">
|
|
||||||
<variables>
|
|
||||||
<noindex-content>false</noindex-content>
|
|
||||||
<custom-favicons>favicon.ico</custom-favicons>
|
|
||||||
<header-logo>logo.svg</header-logo>
|
|
||||||
|
|
||||||
<download-title>Buy me a coffee ☕</download-title>
|
|
||||||
<download-page>https://buymeacoffee.com/maxdorninger</download-page>
|
|
||||||
<showDownloadButton>true</showDownloadButton>
|
|
||||||
<product-web-url>https://github.com/maxdorninger/MediaManager</product-web-url>
|
|
||||||
|
|
||||||
</variables>
|
|
||||||
</build-profile>
|
|
||||||
|
|
||||||
</buildprofiles>
|
|
||||||
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,26 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE instance-profile
|
|
||||||
SYSTEM "https://resources.jetbrains.com/writerside/1.0/product-profile.dtd">
|
|
||||||
|
|
||||||
<instance-profile id="mm"
|
|
||||||
name="MediaManager"
|
|
||||||
start-page="introduction.md">
|
|
||||||
|
|
||||||
<toc-element topic="introduction.md"/>
|
|
||||||
<toc-element topic="configuration-overview.md"/>
|
|
||||||
<toc-element topic="User-Guide.md"/>
|
|
||||||
<toc-element topic="Configuration.md">
|
|
||||||
<toc-element topic="configuration-backend.md"/>
|
|
||||||
<toc-element topic="configuration-frontend.md"/>
|
|
||||||
<toc-element topic="authentication-setup.md"/>
|
|
||||||
<toc-element topic="database-configuration.md"/>
|
|
||||||
<toc-element topic="download-client-configuration.md"/>
|
|
||||||
<toc-element topic="Indexer-Settings.md"/>
|
|
||||||
<toc-element topic="Notifications.md"/>
|
|
||||||
<toc-element topic="Reverse-Proxy.md"/>
|
|
||||||
<toc-element topic="metadata-provider-configuration.md"/>
|
|
||||||
</toc-element>
|
|
||||||
<toc-element topic="troubleshooting.md"/>
|
|
||||||
<toc-element topic="developer-guide.md"/>
|
|
||||||
<toc-element topic="api-reference.md"/>
|
|
||||||
</instance-profile>
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE rules SYSTEM "https://resources.jetbrains.com/writerside/1.0/redirection-rules.dtd">
|
|
||||||
<rules>
|
|
||||||
<!-- format is as follows
|
|
||||||
<rule id="<unique id>">
|
|
||||||
<accepts>page.html</accepts>
|
|
||||||
</rule>
|
|
||||||
-->
|
|
||||||
<rule id="63c34f28">
|
|
||||||
<description>Created after removal of "Installation" from MediaManager</description>
|
|
||||||
<accepts>installation.html</accepts>
|
|
||||||
</rule>
|
|
||||||
<rule id="440cb672">
|
|
||||||
<description>Created after removal of "About MediaManager" from MediaManager</description>
|
|
||||||
<accepts>starter-topic.html</accepts>
|
|
||||||
</rule>
|
|
||||||
<rule id="666c8c31">
|
|
||||||
<description>Created after removal of "User Guide" from MediaManager</description>
|
|
||||||
<accepts>user-guide.html</accepts>
|
|
||||||
</rule>
|
|
||||||
<rule id="6822e619">
|
|
||||||
<description>Created after removal of "Quick Start Guide" from MediaManager</description>
|
|
||||||
<accepts>Quick-Start-Guide.html</accepts>
|
|
||||||
</rule>
|
|
||||||
<rule id="570cb9d1">
|
|
||||||
<description>Created after removal of "user-guide" from MediaManager</description>
|
|
||||||
<accepts>user-guide.html</accepts>
|
|
||||||
</rule>
|
|
||||||
<rule id="7cb7d801">
|
|
||||||
<description>Created after removal of "MetadataRelay" from MediaManager</description>
|
|
||||||
<accepts>MetadataRelay.html</accepts>
|
|
||||||
</rule>
|
|
||||||
</rules>
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
# Configuration
|
|
||||||
|
|
||||||
MediaManager uses a TOML configuration file (`config.toml`) for all backend settings.
|
|
||||||
This centralized configuration approach makes it easier to manage, backup, and share your MediaManager setup.
|
|
||||||
|
|
||||||
Frontend settings are configured through environment variables in your `docker-compose.yaml` file.
|
|
||||||
|
|
||||||
## Configuration File Location
|
|
||||||
|
|
||||||
Your `config.toml` file should be mounted to `/app/config.toml` inside the container:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
volumes:
|
|
||||||
- ./config.toml:/app/config.toml
|
|
||||||
```
|
|
||||||
|
|
||||||
Though you can change the location of the configuration file by setting the `CONFIG_FILE` env variable to another path,
|
|
||||||
e.g. `/etc/mm/config.toml`.
|
|
||||||
|
|
||||||
## Configuration Sections
|
|
||||||
|
|
||||||
The configuration is organized into the following sections:
|
|
||||||
|
|
||||||
- `[misc]` - General settings
|
|
||||||
- `[database]` - Database settings
|
|
||||||
- `[auth]` - Authentication settings
|
|
||||||
- `[notifications]` - Notification settings (Email, Gotify, Ntfy, Pushover)
|
|
||||||
- `[torrents]` - Download client settings (qBittorrent, Transmission, SABnzbd)
|
|
||||||
- `[indexers]` - Indexer settings (Prowlarr and Jackett )
|
|
||||||
- `[metadata]` - TMDB and TVDB settings
|
|
||||||
|
|
||||||
## Configuring Secrets
|
|
||||||
|
|
||||||
For sensitive information like API keys, passwords, and secrets, you _should_ use environment variables.
|
|
||||||
You can actually set every configuration value through environment variables.
|
|
||||||
For example, to set the `token_secret` value for authentication, with a .toml file you would use:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[auth]
|
|
||||||
token_secret = "your_super_secret_key_here"
|
|
||||||
```
|
|
||||||
|
|
||||||
But you can also set it through an environment variable:
|
|
||||||
|
|
||||||
```
|
|
||||||
AUTH__TOKEN_SECRET = "your_super_secret_key_here"
|
|
||||||
```
|
|
||||||
|
|
||||||
or another example with the OIDC client secret:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[auth]
|
|
||||||
...
|
|
||||||
[auth.openid_connect]
|
|
||||||
client_secret = "your_client_secret_from_provider"
|
|
||||||
```
|
|
||||||
|
|
||||||
env variable:
|
|
||||||
|
|
||||||
```
|
|
||||||
AUTH__OPENID_CONNECT__CLIENT_SECRET = "your_client_secret_from_provider"
|
|
||||||
```
|
|
||||||
|
|
||||||
So for every config "level", you basically have to take the name of the value and prepend it with the section names in
|
|
||||||
uppercase with 2 underscores as delimiters.
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
# Indexers
|
|
||||||
|
|
||||||
Indexer settings are configured in the `[indexers]` section of your `config.toml` file. MediaManager supports both Prowlarr and Jackett as indexer providers.
|
|
||||||
|
|
||||||
## Prowlarr (`[indexers.prowlarr]`)
|
|
||||||
|
|
||||||
- `enabled`
|
|
||||||
|
|
||||||
Set to `true` to enable Prowlarr. Default is `false`.
|
|
||||||
|
|
||||||
- `url`
|
|
||||||
|
|
||||||
Base URL of your Prowlarr instance.
|
|
||||||
|
|
||||||
- `api_key`
|
|
||||||
|
|
||||||
API key for Prowlarr. You can find this in Prowlarr's settings under General.
|
|
||||||
|
|
||||||
## Jackett (`[indexers.jackett]`)
|
|
||||||
|
|
||||||
- `enabled`
|
|
||||||
|
|
||||||
Set to `true` to enable Jackett. Default is `false`.
|
|
||||||
|
|
||||||
- `url`
|
|
||||||
|
|
||||||
Base URL of your Jackett instance.
|
|
||||||
|
|
||||||
- `api_key`
|
|
||||||
|
|
||||||
API key for Jackett. You can find this in Jackett's dashboard.
|
|
||||||
|
|
||||||
- `indexers`
|
|
||||||
|
|
||||||
List of indexer names to use with Jackett. You can specify which indexers Jackett should search through.
|
|
||||||
|
|
||||||
## Example Configuration
|
|
||||||
|
|
||||||
Here's a complete example of the indexers section in your `config.toml`:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[indexers]
|
|
||||||
[indexers.prowlarr]
|
|
||||||
enabled = true
|
|
||||||
url = "http://prowlarr:9696"
|
|
||||||
api_key = "your_prowlarr_api_key"
|
|
||||||
|
|
||||||
[indexers.jackett]
|
|
||||||
enabled = false
|
|
||||||
url = "http://jackett:9117"
|
|
||||||
api_key = "your_jackett_api_key"
|
|
||||||
indexers = ["1337x", "rarbg"]
|
|
||||||
```
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
# Reverse Proxy
|
|
||||||
|
|
||||||
MediaManager is probably unlike any other service that you have deployed before, because it has a separate frontend and backend container.
|
|
||||||
This means that setting up a reverse proxy is a bit different.
|
|
||||||
|
|
||||||
When deploying MediaManager, you have two choices:
|
|
||||||
|
|
||||||
- Use two different hostnames, e.g. `mediamanager.example.com` for the frontend and `api-mediamanager.example.com` for the backend.
|
|
||||||
- Use a single hostname with a base path, e.g. `mediamanager.example.com` for the frontend and `mediamanager.example.com/api/v1` for the backend.
|
|
||||||
|
|
||||||
If you choose the first option, you can set up your reverse proxy as usual, forwarding requests to the appropriate containers based on the hostname.
|
|
||||||
If you choose the second option, you need to ensure that your reverse proxy is configured to handle the base path correctly.
|
|
||||||
|
|
||||||
## Example Caddy Configuration
|
|
||||||
|
|
||||||
```
|
|
||||||
mm.my-domain.com {
|
|
||||||
@api path /api/*
|
|
||||||
reverse_proxy @api 10.0.0.7:8000
|
|
||||||
|
|
||||||
reverse_proxy 10.0.0.7:3000
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example Traefik Configuration
|
|
||||||
This example assumes you use Traefik with Docker labels.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
|
|
||||||
services:
|
|
||||||
backend:
|
|
||||||
image: ghcr.io/maxdorninger/mediamanager/backend:latest
|
|
||||||
ports:
|
|
||||||
- "8000:8000"
|
|
||||||
labels:
|
|
||||||
- "traefik.enable=true"
|
|
||||||
- "traefik.http.routers.mediamanager-backend.rule=Host(`media.example`)&&PathPrefix(`/api/v1`)"
|
|
||||||
- "traefik.http.routers.mediamanager-backend.tls=true"
|
|
||||||
- "traefik.http.routers.mediamanager-backend.tls.certresolver=letsencrypt"
|
|
||||||
- "traefik.http.routers.mediamanager-backend.entrypoints=websecure"
|
|
||||||
environment:
|
|
||||||
- MISC_FRONTEND_URL=https://media.example/
|
|
||||||
frontend:
|
|
||||||
image: ghcr.io/maxdorninger/mediamanager/frontend:latest
|
|
||||||
labels:
|
|
||||||
- "traefik.enable=true"
|
|
||||||
- "traefik.http.routers.mediamanager-frontend.rule=Host(`media.example`)"
|
|
||||||
- "traefik.http.routers.mediamanager-frontend.tls=true"
|
|
||||||
- "traefik.http.routers.mediamanager-frontend.tls.certresolver=letsencrypt"
|
|
||||||
- "traefik.http.routers.mediamanager-frontend.entrypoints=websecure"
|
|
||||||
ports:
|
|
||||||
- "3000:3000"
|
|
||||||
environment:
|
|
||||||
- PUBLIC_API_URL=https://media.example/api/v1
|
|
||||||
```
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
# Usage
|
|
||||||
|
|
||||||
If you are coming from Radarr or Sonarr you will find that MediaManager does things a bit differently.
|
|
||||||
Instead of completely automatically downloading and managing your media, MediaManager focuses on providing an
|
|
||||||
easy-to-use interface to guide you through the process of finding and downloading media. Advanced features like multiple
|
|
||||||
qualities of a show/movie necessitate such a paradigm shift.
|
|
||||||
__So here is a quick step-by-step guide to get you started:__
|
|
||||||
|
|
||||||
<tabs>
|
|
||||||
<tab id="as-a-user" title="as a user">
|
|
||||||
<procedure title="Downloading/Requesting a show" id="request-show-user">
|
|
||||||
<step>Add a show on the "Add Show" page</step>
|
|
||||||
<step>After adding the show you will be redirected to the show's page.</step>
|
|
||||||
<step>There you can click the "Request Season" button.</step>
|
|
||||||
<step>Select one or more seasons that you want to download</step>
|
|
||||||
<step>Then select the "Min Quality", this will be the minimum resolution of the content to download.</step>
|
|
||||||
<step>Then select the "Wanted Quality", this will be the <strong>maximum</strong> resolution of the content to download.</step>
|
|
||||||
<step>Finally click Submit request, though this is not the last step!</step>
|
|
||||||
<step>An administrator first has to approve your request for download, only then will the requested content be downloaded.</step>
|
|
||||||
<p>Congratulation! You've downloaded a show.</p>
|
|
||||||
</procedure>
|
|
||||||
</tab>
|
|
||||||
<tab id="as-an-admin" title="as an admin">
|
|
||||||
<procedure title="Requesting a show" id="request-show-admin">
|
|
||||||
<step>Add a show on the "Add Show" page</step>
|
|
||||||
<step>After adding the show you will be redirected to the show's page.</step>
|
|
||||||
<step>There you can click the "Request Season" button.</step>
|
|
||||||
<step>Select one or more seasons that you want to download</step>
|
|
||||||
<step>Then select the "Min Quality", this will be the minimum resolution of the content to download.</step>
|
|
||||||
<step>Then select the "Wanted Quality", this will be the <strong>maximum</strong> resolution of the content to download.</step>
|
|
||||||
<step>Finally click Submit request, as you are an admin, your request will be automatically approved.</step>
|
|
||||||
<p>Congratulation! You've downloaded a show.</p>
|
|
||||||
</procedure>
|
|
||||||
<procedure title="Downloading a show" id="download-show-admin">
|
|
||||||
<p>You can only directly download a show if you are an admin!</p>
|
|
||||||
<step>Go to a show's page.</step>
|
|
||||||
<step>There you can click the "Download Season" button.</step>
|
|
||||||
<step>Enter the season's number that you want to download</step>
|
|
||||||
<step>Then optionally select the "File Path Suffix", <strong>it needs to be unique per season per show!</strong> </step>
|
|
||||||
<step>Then click "Download" on a torrent that you want to download.</step>
|
|
||||||
<p>Congratulation! You've downloaded a show.</p>
|
|
||||||
</procedure>
|
|
||||||
<procedure title="Managing requests" id="approving-request-admin">
|
|
||||||
<p>Users need their requests to be approved by an admin, to do this follow these steps:</p>
|
|
||||||
<step>Go to the "Requests" page.</step>
|
|
||||||
<step>There you can approve, delete or modify a user's request.</step>
|
|
||||||
</procedure>
|
|
||||||
</tab>
|
|
||||||
</tabs>
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
# API Reference
|
|
||||||
|
|
||||||
Media Manager's backend is built with FastAPI, which automatically generates interactive API documentation.
|
|
||||||
|
|
||||||
* **Swagger UI** (typically available at `http://localhost:8000/docs`).
|
|
||||||
* **ReDoc** (typically available at `http://localhost:8000/redoc`).
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
# Authentication
|
|
||||||
|
|
||||||
MediaManager supports multiple authentication methods. Email/password authentication is the default, but you can also enable OpenID Connect (OAuth 2.0) for integration with external identity providers.
|
|
||||||
|
|
||||||
All authentication settings are configured in the `[auth]` section of your `config.toml` file.
|
|
||||||
|
|
||||||
## General Authentication Settings (`[auth]`)
|
|
||||||
|
|
||||||
- `token_secret`
|
|
||||||
|
|
||||||
Strong secret key for signing JWTs (create with `openssl rand -hex 32`). This is a required field.
|
|
||||||
|
|
||||||
- `session_lifetime`
|
|
||||||
|
|
||||||
Lifetime of user sessions in seconds. Default is `86400` (1 day).
|
|
||||||
|
|
||||||
- `admin_emails`
|
|
||||||
|
|
||||||
A list of email addresses for administrator accounts. This is a required field.
|
|
||||||
|
|
||||||
- `email_password_resets`
|
|
||||||
|
|
||||||
Toggle for enabling password resets via email. If users request a password reset because they forgot their password, they will be sent an email with a link to reset it. Default is `false`.
|
|
||||||
|
|
||||||
<note>
|
|
||||||
To use email password resets, you must also configure SMTP settings in the <code>[notifications.smtp_config]</code> section.
|
|
||||||
</note>
|
|
||||||
|
|
||||||
## OpenID Connect Settings (`[auth.openid_connect]`)
|
|
||||||
|
|
||||||
OpenID Connect allows you to integrate with external identity providers like Google, Microsoft Azure AD, Keycloak, or any other OIDC-compliant provider.
|
|
||||||
|
|
||||||
- `enabled`
|
|
||||||
|
|
||||||
Set to `true` to enable OpenID Connect authentication. Default is `false`.
|
|
||||||
|
|
||||||
- `client_id`
|
|
||||||
|
|
||||||
Client ID provided by your OpenID Connect provider.
|
|
||||||
|
|
||||||
- `client_secret`
|
|
||||||
|
|
||||||
Client secret provided by your OpenID Connect provider.
|
|
||||||
|
|
||||||
- `configuration_endpoint`
|
|
||||||
|
|
||||||
OpenID Connect configuration endpoint URL. Note the lack of a trailing slash - this is important.
|
|
||||||
|
|
||||||
- `name`
|
|
||||||
|
|
||||||
Display name for the OpenID Connect provider that will be shown on the login page.
|
|
||||||
|
|
||||||
## Example Configuration
|
|
||||||
|
|
||||||
Here's a complete example of the authentication section in your `config.toml`:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[auth]
|
|
||||||
token_secret = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6"
|
|
||||||
session_lifetime = 604800 # 1 week
|
|
||||||
admin_emails = ["admin@example.com", "manager@example.com"]
|
|
||||||
email_password_resets = true
|
|
||||||
|
|
||||||
[auth.openid_connect]
|
|
||||||
enabled = true
|
|
||||||
client_id = "mediamanager-client"
|
|
||||||
client_secret = "your-secret-key-here"
|
|
||||||
configuration_endpoint = "https://auth.example.com/.well-known/openid-configuration"
|
|
||||||
name = "Authentik"
|
|
||||||
```
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
# Backend
|
|
||||||
These settings configure the core backend application through the `config.toml` file. All backend configuration is now centralized in this TOML file instead of environment variables.
|
|
||||||
|
|
||||||
## General Settings (`[misc]`)
|
|
||||||
|
|
||||||
- `frontend_url`
|
|
||||||
|
|
||||||
The URL the frontend will be accessed from. This is a required field and must include the trailing slash.
|
|
||||||
|
|
||||||
- `cors_urls`
|
|
||||||
|
|
||||||
A list of origins you are going to access the API from. Note the lack of trailing slashes.
|
|
||||||
|
|
||||||
- `api_base_path`
|
|
||||||
|
|
||||||
The URL base path of the backend API. Default is `/api/v1`. Note the lack of a trailing slash.
|
|
||||||
|
|
||||||
- `development`
|
|
||||||
|
|
||||||
Set to `true` to enable development mode. Default is `false`.
|
|
||||||
|
|
||||||
## Example Configuration
|
|
||||||
|
|
||||||
Here's a complete example of the general settings section in your `config.toml`:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[misc]
|
|
||||||
# REQUIRED: Change this to match your actual frontend URL
|
|
||||||
frontend_url = "http://localhost:3000/"
|
|
||||||
|
|
||||||
# REQUIRED: List all origins that will access the API
|
|
||||||
cors_urls = ["http://localhost:3000", "http://localhost:8000"]
|
|
||||||
|
|
||||||
# Optional: API base path (rarely needs to be changed)
|
|
||||||
api_base_path = "/api/v1"
|
|
||||||
|
|
||||||
# Optional: Development mode (set to true for debugging)
|
|
||||||
development = false
|
|
||||||
```
|
|
||||||
|
|
||||||
<note>
|
|
||||||
The <code>frontend_url</code> and <code>cors_urls</code> are the most important settings to configure correctly. Make sure they match your actual deployment URLs.
|
|
||||||
</note>
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Frontend
|
|
||||||
|
|
||||||
## Environment Variables
|
|
||||||
|
|
||||||
### `PUBLIC_API_URL`
|
|
||||||
|
|
||||||
You (the browser) must reach the backend from this url. Default is `http://localhost:8000/api/v1`. Example:
|
|
||||||
`https://mediamanager.example.com/api/v1`.
|
|
||||||
|
|
||||||
## Build Arguments (web/Dockerfile)
|
|
||||||
|
|
||||||
**TODO: expand on this section**
|
|
||||||
|
|
||||||
Unfortunately you need to build the frontend docker container, to configure a url base path for the frontend. This is because
|
|
||||||
SvelteKit needs to know the base path at build time.
|
|
||||||
|
|
||||||
### `BASE_URL`
|
|
||||||
|
|
||||||
Sets the base url path, it must begin with a slash and not end with one. Example (in build command):
|
|
||||||
- clone the repo
|
|
||||||
- cd into the repo's root directory
|
|
||||||
- `docker build --build-arg BASE_URL=/media -f web/Dockerfile .`
|
|
||||||
|
|
||||||
### `VERSION`
|
|
||||||
|
|
||||||
Sets the `PUBLIC_VERSION` environment variable at runtime in the frontend container. Passed during build. Example (in
|
|
||||||
build command): `docker build --build-arg VERSION=1.2.3 -f web/Dockerfile .`
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# Installation Guide
|
|
||||||
|
|
||||||
The recommended way to install and run Media Manager is using Docker and Docker Compose.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
* Ensure Docker and Docker Compose are installed on your system.
|
|
||||||
* If you plan to use OAuth 2.0 / OpenID Connect for authentication, you will need an account and client credentials
|
|
||||||
from an OpenID provider (e.g., Authentik, Pocket ID).
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
* Download the docker-compose.yaml from the MediaManager repo with the following command:
|
|
||||||
```
|
|
||||||
wget -o docker-compose.yaml https://raw.githubusercontent.com/maxdorninger/MediaManager/refs/heads/master/docker-compose.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
* Configure the necessary environment variables in your `docker-compose.yaml` file.
|
|
||||||
* For more information on the available configuration options, see the [Configuration section](Configuration.md) of the
|
|
||||||
documentation.
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
# Contributing
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# Database
|
|
||||||
|
|
||||||
Database settings are configured in the `[database]` section of your `config.toml` file. MediaManager uses PostgreSQL as its database backend.
|
|
||||||
|
|
||||||
## Database Settings (`[database]`)
|
|
||||||
|
|
||||||
- `host`
|
|
||||||
|
|
||||||
Hostname or IP of the PostgreSQL server. Default is `localhost`.
|
|
||||||
|
|
||||||
- `port`
|
|
||||||
|
|
||||||
Port number of the PostgreSQL server. Default is `5432`.
|
|
||||||
|
|
||||||
- `user`
|
|
||||||
|
|
||||||
Username for PostgreSQL connection. Default is `MediaManager`.
|
|
||||||
|
|
||||||
- `password`
|
|
||||||
|
|
||||||
Password for the PostgreSQL user. Default is `MediaManager`.
|
|
||||||
|
|
||||||
- `dbname`
|
|
||||||
|
|
||||||
Name of the PostgreSQL database. Default is `MediaManager`.
|
|
||||||
|
|
||||||
## Example Configuration
|
|
||||||
|
|
||||||
Here's a complete example of the database section in your `config.toml`:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[database]
|
|
||||||
host = "db"
|
|
||||||
port = 5432
|
|
||||||
user = "MediaManager"
|
|
||||||
password = "your_secure_password"
|
|
||||||
dbname = "MediaManager"
|
|
||||||
```
|
|
||||||
|
|
||||||
<tip>
|
|
||||||
In docker-compose deployments the containers name is simultaneously its hostname, so you can use "db" or "postgres" as host.
|
|
||||||
</tip>
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
# Developer Guide
|
|
||||||
|
|
||||||
This section is for those who want to contribute to Media Manager or understand its internals.
|
|
||||||
|
|
||||||
### Source Code
|
|
||||||
|
|
||||||
- `media_manager/`: Backend FastAPI application
|
|
||||||
- `web/`: Frontend SvelteKit application
|
|
||||||
- `Writerside/`: Documentation
|
|
||||||
- `metadata_relay/`: Metadata relay service
|
|
||||||
-
|
|
||||||
|
|
||||||
### Backend Development
|
|
||||||
|
|
||||||
- Uses `uv` for dependency management
|
|
||||||
- Follows standard FastAPI project structure
|
|
||||||
- Database migrations are handled by Alembic
|
|
||||||
|
|
||||||
### Frontend Development
|
|
||||||
|
|
||||||
- Uses `npm` for package management
|
|
||||||
- SvelteKit with TypeScript
|
|
||||||
|
|
||||||
### Contributing
|
|
||||||
|
|
||||||
- Consider opening an issue to discuss significant changes before starting work
|
|
||||||
|
|
||||||
## Sequence Diagrams
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
title Step-by-step: going from adding a show to importing a torrent of one of its seasons
|
|
||||||
|
|
||||||
User->>TV Router: Add a show (POST /tv/shows)
|
|
||||||
TV Router->>TV Service: Receive Show Request
|
|
||||||
TV Service->>MetadataProviderService: Get Metadata for Show
|
|
||||||
MetadataProviderService->>File System: Save Poster Image
|
|
||||||
TV Service->>Database: Store show information
|
|
||||||
|
|
||||||
User->>TV Router: Get Available Torrents for a Season (GET /tv/torrents)
|
|
||||||
TV Router->>TV Service: Receive Request
|
|
||||||
TV Service->>Indexer Service: Search for torrents
|
|
||||||
TV Service->>User: Returns Public Indexer Results
|
|
||||||
|
|
||||||
User->>TV Router: Download Torrent (POST /tv/torrents)
|
|
||||||
TV Router->>TV Service: Receive Request
|
|
||||||
Note over Database: This associates a season with a torrent id and the file_path_suffix
|
|
||||||
TV Service->>Database: Saves a SeasonFile object
|
|
||||||
TV Service->>Torrent Service: Download Torrent
|
|
||||||
Torrent Service->>File System: Save Torrentfile
|
|
||||||
Torrent Service->>QBittorrent: Download Torrent
|
|
||||||
|
|
||||||
Note over Scheduler: Hourly scheduler trigger
|
|
||||||
Scheduler->>TV Service: auto_import_all_show_torrents()
|
|
||||||
TV Service->>Database: Get all Shows and seasons which are associated with a torrent
|
|
||||||
TV Service->>Torrent Service: Update Torrent download statuses
|
|
||||||
Note over TV Service: if a torrent is finished downloading it will be imported
|
|
||||||
TV Service->>Torrent Service: get all files in the torrents directory
|
|
||||||
Note over Torrent Service: Extracts archives, guesses mimetype (Video/Subtitle/Other)
|
|
||||||
Note over TV Service: filters files based on some regex and renames them
|
|
||||||
TV Service->>File System: Move/Hardlink video and subtitle files
|
|
||||||
|
|
||||||
Note over User: User can now access the show in e.g. Jellyfin
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tech Stack
|
|
||||||
|
|
||||||
### Backend
|
|
||||||
|
|
||||||
- Python with FastAPI
|
|
||||||
- SQLAlchemy
|
|
||||||
- Pydantic and Pydantic-Settings
|
|
||||||
|
|
||||||
### Frontend
|
|
||||||
|
|
||||||
- TypeScript with SvelteKit
|
|
||||||
- Tailwind CSS
|
|
||||||
- shadcn-svelte
|
|
||||||
|
|
||||||
### CI/CD
|
|
||||||
|
|
||||||
- GitHub Actions
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# MediaManager
|
|
||||||
|
|
||||||
MediaManager is modern software to manage your TV and movie library. It is designed to be a replacement for Sonarr,
|
|
||||||
Radarr, Overseer, and Jellyseer.
|
|
||||||
It supports TVDB and TMDB for metadata, supports OIDC and OAuth 2.0 for authentication and supports Prowlarr and
|
|
||||||
Jackett.
|
|
||||||
MediaManager is built first and foremost for deployment with Docker, making it easy to set up.
|
|
||||||
|
|
||||||
It also provides an API to interact with the software programmatically, allowing for automation and integration with
|
|
||||||
other services.
|
|
||||||
|
|
||||||
## Screenshots
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
# Metadata Provider Configuration
|
|
||||||
|
|
||||||
Metadata provider settings are configured in the `[metadata]` section of your `config.toml` file. These settings control how MediaManager retrieves information about movies and TV shows.
|
|
||||||
|
|
||||||
## TMDB Settings (`[metadata.tmdb]`)
|
|
||||||
|
|
||||||
TMDB (The Movie Database) is the primary metadata provider for MediaManager. It provides detailed information about movies and TV shows.
|
|
||||||
|
|
||||||
<tip>
|
|
||||||
Other software like Jellyfin use TMDB as well, so there won't be any metadata discrepancies.
|
|
||||||
</tip>
|
|
||||||
|
|
||||||
### `tmdb_relay_url`
|
|
||||||
|
|
||||||
If you want to use your own TMDB relay service, set this to the URL of your own MetadataRelay. Otherwise, use the default relay.
|
|
||||||
|
|
||||||
- **Default:** `https://metadata-relay.maxid.me/tmdb`
|
|
||||||
- **Example:** `https://your-own-relay.example.com/tmdb`
|
|
||||||
|
|
||||||
## TVDB Settings (`[metadata.tvdb]`)
|
|
||||||
|
|
||||||
<warning>
|
|
||||||
The TVDB might provide false metadata and doesn't support some features of MediaManager like showing overviews. Therefore, TMDB is the preferred metadata provider.
|
|
||||||
</warning>
|
|
||||||
|
|
||||||
### `tvdb_relay_url`
|
|
||||||
|
|
||||||
If you want to use your own TVDB relay service, set this to the URL of your own MetadataRelay. Otherwise, use the default relay.
|
|
||||||
|
|
||||||
- **Default:** `https://metadata-relay.maxid.me/tvdb`
|
|
||||||
- **Example:** `https://your-own-relay.example.com/tvdb`
|
|
||||||
|
|
||||||
## MetadataRelay
|
|
||||||
|
|
||||||
<note>
|
|
||||||
To use MediaManager <strong>you don't need to set up your own MetadataRelay</strong>, as the default relay hosted by the developer should be sufficient for most purposes.
|
|
||||||
</note>
|
|
||||||
|
|
||||||
The MetadataRelay is a service that provides metadata for MediaManager. It acts as a proxy for TMDB and TVDB, allowing you to use your own API keys if needed, but the default relay means you don't need to create accounts for API keys yourself.
|
|
||||||
|
|
||||||
You might want to use your own relay if you want to avoid rate limits, protect your privacy, or for other reasons. If you know Sonarr's Skyhook, this is similar to that.
|
|
||||||
|
|
||||||
### Where to get API keys
|
|
||||||
|
|
||||||
- Get a TMDB API key from [The Movie Database](https://www.themoviedb.org/settings/api)
|
|
||||||
- Get a TVDB API key from [The TVDB](https://thetvdb.com/auth/register)
|
|
||||||
|
|
||||||
<tip>
|
|
||||||
If you want to use your own MetadataRelay, you can set the <code>tmdb_relay_url</code> and/or <code>tvdb_relay_url</code> to your own relay service.
|
|
||||||
</tip>
|
|
||||||
|
|
||||||
## Example Configuration
|
|
||||||
|
|
||||||
Here's a complete example of the metadata section in your `config.toml`:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[metadata]
|
|
||||||
# TMDB configuration
|
|
||||||
[metadata.tmdb]
|
|
||||||
tmdb_relay_url = "https://metadata-relay.maxid.me/tmdb"
|
|
||||||
|
|
||||||
# TVDB configuration
|
|
||||||
[metadata.tvdb]
|
|
||||||
tvdb_relay_url = "https://metadata-relay.maxid.me/tvdb"
|
|
||||||
```
|
|
||||||
|
|
||||||
<note>
|
|
||||||
In most cases, you can simply use the default values and don't need to specify these settings in your config file at all.
|
|
||||||
</note>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
|
||||||
<!DOCTYPE topic SYSTEM "https://resources.jetbrains.com/writerside/1.0/html-entities.dtd">
|
|
||||||
<topic id="notes"
|
|
||||||
is-library="true" title="notes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://resources.jetbrains.com/writerside/1.0/topic.v2.xsd">
|
|
||||||
<snippet id="list-format">
|
|
||||||
Lists have to be formatted like this: <code>["item1", "item2", "item3"]</code>. Note the double quotes.
|
|
||||||
</snippet>
|
|
||||||
</topic>
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# Troubleshooting
|
|
||||||
|
|
||||||
<note>
|
|
||||||
Note the lack of a trailing slash in some env vars like FRONTEND_URL. This is important.
|
|
||||||
</note>
|
|
||||||
|
|
||||||
<tip>
|
|
||||||
Always check the container and browser logs for more specific error messages
|
|
||||||
</tip>
|
|
||||||
|
|
||||||
## Authentication Issues (OIDC)
|
|
||||||
|
|
||||||
* Verify `OPENID_CLIENT_ID`, `OPENID_CLIENT_SECRET`, and `OPENID_CONFIGURATION_ENDPOINT` are correct.
|
|
||||||
* Ensure the `FRONTEND_URL` is accurate and that your OpenID provider has the correct redirect URI whitelisted (
|
|
||||||
e.g., `http://your-frontend-url/api/v1/auth/cookie/Authentik/callback`).
|
|
||||||
|
|
||||||
## CORS Errors
|
|
||||||
|
|
||||||
* Ensure `FRONTEND_URL` is correctly set.
|
|
||||||
* Ensure your frontend's url is listed in `CORS_URLS`.
|
|
||||||
* Check if your reverse proxy is correctly configured, see [Reverse Proxy Configuration](Reverse-Proxy.md) for examples.
|
|
||||||
|
|
||||||
## Data Not Appearing / File Issues
|
|
||||||
|
|
||||||
* Verify that the volume mounts for `IMAGE_DIRECTORY`, `TV_DIRECTORY`, `MOVIE_DIRECTORY`, and `TORRENT_DIRECTORY` in
|
|
||||||
your `docker-compose.yaml` are correctly pointing to your media folders on the host machine.
|
|
||||||
* Check file and directory permissions for the user running the Docker container (or the `node` user inside the
|
|
||||||
containers).
|
|
||||||
* For hardlinks to work, you must not use different docker volumes for TV, Torrents, etc.
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE vars SYSTEM "https://resources.jetbrains.com/writerside/1.0/vars.dtd">
|
|
||||||
<vars>
|
|
||||||
<var name="product" value="Writerside"/>
|
|
||||||
</vars>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE ihp SYSTEM "https://resources.jetbrains.com/writerside/1.0/ihp.dtd">
|
|
||||||
|
|
||||||
<ihp version="2.0">
|
|
||||||
<topics dir="topics" web-path="topics"/>
|
|
||||||
<images dir="images" web-path="MediaManager"/>
|
|
||||||
<instance src="mm.tree"/>
|
|
||||||
<instance src="Writerside_libraries.tree"/>
|
|
||||||
</ihp>
|
|
||||||
@@ -1,13 +1,16 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
sys.path = ["", ".."] + sys.path[1:]
|
sys.path = ["", "..", *sys.path[1:]]
|
||||||
|
|
||||||
|
|
||||||
from logging.config import fileConfig # noqa: E402
|
from logging.config import fileConfig # noqa: E402
|
||||||
|
|
||||||
|
from sqlalchemy import ( # noqa: E402
|
||||||
|
engine_from_config,
|
||||||
|
pool,
|
||||||
|
)
|
||||||
|
|
||||||
from alembic import context # noqa: E402
|
from alembic import context # noqa: E402
|
||||||
from sqlalchemy import engine_from_config # noqa: E402
|
|
||||||
from sqlalchemy import pool # noqa: E402
|
|
||||||
|
|
||||||
# this is the Alembic Config object, which provides
|
# this is the Alembic Config object, which provides
|
||||||
# access to the values within the .ini file in use.
|
# access to the values within the .ini file in use.
|
||||||
@@ -23,34 +26,40 @@ if config.config_file_name is not None:
|
|||||||
# from myapp import mymodel
|
# from myapp import mymodel
|
||||||
# target_metadata = mymodel.Base.metadata
|
# target_metadata = mymodel.Base.metadata
|
||||||
|
|
||||||
from media_manager.auth.db import User, OAuthAccount # noqa: E402
|
from media_manager.auth.db import OAuthAccount, User # noqa: E402
|
||||||
|
from media_manager.config import MediaManagerConfig # noqa: E402
|
||||||
|
from media_manager.database import Base # noqa: E402
|
||||||
from media_manager.indexer.models import IndexerQueryResult # noqa: E402
|
from media_manager.indexer.models import IndexerQueryResult # noqa: E402
|
||||||
from media_manager.torrent.models import Torrent # noqa: E402
|
|
||||||
from media_manager.tv.models import Show, Season, Episode, SeasonFile, SeasonRequest # noqa: E402
|
|
||||||
from media_manager.movies.models import Movie, MovieFile, MovieRequest # noqa: E402
|
from media_manager.movies.models import Movie, MovieFile, MovieRequest # noqa: E402
|
||||||
from media_manager.notification.models import Notification # noqa: E402
|
from media_manager.notification.models import Notification # noqa: E402
|
||||||
from media_manager.database import Base # noqa: E402
|
from media_manager.torrent.models import Torrent # noqa: E402
|
||||||
from media_manager.config import AllEncompassingConfig # noqa: E402
|
from media_manager.tv.models import ( # noqa: E402
|
||||||
|
Episode,
|
||||||
|
Season,
|
||||||
|
SeasonFile,
|
||||||
|
SeasonRequest,
|
||||||
|
Show,
|
||||||
|
)
|
||||||
|
|
||||||
target_metadata = Base.metadata
|
target_metadata = Base.metadata
|
||||||
|
|
||||||
# this is to keep pycharm from complaining about/optimizing unused imports
|
# this is to keep pycharm from complaining about/optimizing unused imports
|
||||||
# noinspection PyStatementEffect
|
# noinspection PyStatementEffect
|
||||||
(
|
__all__ = [
|
||||||
User,
|
"Episode",
|
||||||
OAuthAccount,
|
"IndexerQueryResult",
|
||||||
IndexerQueryResult,
|
"Movie",
|
||||||
Torrent,
|
"MovieFile",
|
||||||
Show,
|
"MovieRequest",
|
||||||
Season,
|
"Notification",
|
||||||
Episode,
|
"OAuthAccount",
|
||||||
SeasonFile,
|
"Season",
|
||||||
SeasonRequest,
|
"SeasonFile",
|
||||||
Movie,
|
"SeasonRequest",
|
||||||
MovieFile,
|
"Show",
|
||||||
MovieRequest,
|
"Torrent",
|
||||||
Notification,
|
"User",
|
||||||
)
|
]
|
||||||
|
|
||||||
|
|
||||||
# other values from the config, defined by the needs of env.py,
|
# other values from the config, defined by the needs of env.py,
|
||||||
@@ -59,20 +68,8 @@ target_metadata = Base.metadata
|
|||||||
# ... etc.
|
# ... etc.
|
||||||
|
|
||||||
|
|
||||||
db_config = AllEncompassingConfig().database
|
db_config = MediaManagerConfig().database
|
||||||
db_url = (
|
db_url = f"postgresql+psycopg://{db_config.user}:{db_config.password}@{db_config.host}:{db_config.port}/{db_config.dbname}"
|
||||||
"postgresql+psycopg"
|
|
||||||
+ "://"
|
|
||||||
+ db_config.user
|
|
||||||
+ ":"
|
|
||||||
+ db_config.password
|
|
||||||
+ "@"
|
|
||||||
+ db_config.host
|
|
||||||
+ ":"
|
|
||||||
+ str(db_config.port)
|
|
||||||
+ "/"
|
|
||||||
+ db_config.dbname
|
|
||||||
)
|
|
||||||
|
|
||||||
config.set_main_option("sqlalchemy.url", db_url)
|
config.set_main_option("sqlalchemy.url", db_url)
|
||||||
|
|
||||||
@@ -109,7 +106,13 @@ def run_migrations_online() -> None:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def include_object(object, name, type_, reflected, compare_to):
|
def include_object(
|
||||||
|
_object: object | None,
|
||||||
|
name: str | None,
|
||||||
|
type_: str | None,
|
||||||
|
_reflected: bool | None,
|
||||||
|
_compare_to: object | None,
|
||||||
|
) -> bool:
|
||||||
if type_ == "table" and name == "apscheduler_jobs":
|
if type_ == "table" and name == "apscheduler_jobs":
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
"""add original_language columns to show and movie tables
|
||||||
|
|
||||||
|
Revision ID: 16e78af9e5bf
|
||||||
|
Revises: eb0bd3cc1852
|
||||||
|
Create Date: 2025-12-13 18:47:02.146038
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = "16e78af9e5bf"
|
||||||
|
down_revision: Union[str, None] = "eb0bd3cc1852"
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
# Add original_language column to show table
|
||||||
|
op.add_column("show", sa.Column("original_language", sa.String(10), nullable=True))
|
||||||
|
|
||||||
|
# Add original_language column to movie table
|
||||||
|
op.add_column("movie", sa.Column("original_language", sa.String(10), nullable=True))
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
# Remove original_language column from movie table
|
||||||
|
op.drop_column("movie", "original_language")
|
||||||
|
|
||||||
|
# Remove original_language column from show table
|
||||||
|
op.drop_column("show", "original_language")
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
"""Add library field to Movie and Show tables
|
||||||
|
|
||||||
|
Revision ID: 1801d9f5a275
|
||||||
|
Revises: 333866afcd2c
|
||||||
|
Create Date: 2025-07-16 01:09:44.045395
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = "1801d9f5a275"
|
||||||
|
down_revision: Union[str, None] = "333866afcd2c"
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
op.add_column(
|
||||||
|
"movie",
|
||||||
|
sa.Column(
|
||||||
|
"library", sa.String(), nullable=False, server_default=sa.text("'Default'")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
op.add_column(
|
||||||
|
"show",
|
||||||
|
sa.Column(
|
||||||
|
"library", sa.String(), nullable=False, server_default=sa.text("'Default'")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
op.drop_column("show", "library")
|
||||||
|
op.drop_column("movie", "library")
|
||||||
@@ -8,9 +8,9 @@ Create Date: 2025-06-22 13:46:01.973406
|
|||||||
|
|
||||||
from typing import Sequence, Union
|
from typing import Sequence, Union
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "1f340754640a"
|
revision: str = "1f340754640a"
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ Create Date: 2025-07-06 10:49:08.814496
|
|||||||
|
|
||||||
from typing import Sequence, Union
|
from typing import Sequence, Union
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "21a19f0675f9"
|
revision: str = "21a19f0675f9"
|
||||||
|
|||||||
35
alembic/versions/2c61f662ca9e_add_imdb_id_fields.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
"""add imdb_id fields
|
||||||
|
|
||||||
|
Revision ID: 2c61f662ca9e
|
||||||
|
Revises: 16e78af9e5bf
|
||||||
|
Create Date: 2025-12-23 19:42:09.593945
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = "2c61f662ca9e"
|
||||||
|
down_revision: Union[str, None] = "16e78af9e5bf"
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column("movie", sa.Column("imdb_id", sa.String(), nullable=True))
|
||||||
|
op.add_column("show", sa.Column("imdb_id", sa.String(), nullable=True))
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_column("show", "imdb_id")
|
||||||
|
op.drop_column("movie", "imdb_id")
|
||||||
|
# ### end Alembic commands ###
|
||||||
@@ -8,9 +8,10 @@ Create Date: 2025-07-09 20:55:42.338629
|
|||||||
|
|
||||||
from typing import Sequence, Union
|
from typing import Sequence, Union
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "333866afcd2c"
|
revision: str = "333866afcd2c"
|
||||||
down_revision: Union[str, None] = "aa4689f80796"
|
down_revision: Union[str, None] = "aa4689f80796"
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
"""Add score field to IndexerQueryResult table
|
||||||
|
|
||||||
|
Revision ID: 5299dfed220b
|
||||||
|
Revises: 1801d9f5a275
|
||||||
|
Create Date: 2025-07-16 23:24:37.931188
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = "5299dfed220b"
|
||||||
|
down_revision: Union[str, None] = "1801d9f5a275"
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column(
|
||||||
|
"indexer_query_result",
|
||||||
|
sa.Column("score", sa.Integer(), nullable=False, server_default=sa.text("0")),
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_column("indexer_query_result", "score")
|
||||||
|
# ### end Alembic commands ###
|
||||||
@@ -8,9 +8,9 @@ Create Date: 2025-06-10 21:25:27.871064
|
|||||||
|
|
||||||
from typing import Sequence, Union
|
from typing import Sequence, Union
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "7508237d5bc2"
|
revision: str = "7508237d5bc2"
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ Create Date: 2025-05-27 21:36:18.532068
|
|||||||
|
|
||||||
from typing import Sequence, Union
|
from typing import Sequence, Union
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy.dialects import postgresql
|
from sqlalchemy.dialects import postgresql
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "93fb07842385"
|
revision: str = "93fb07842385"
|
||||||
down_revision: Union[str, None] = None
|
down_revision: Union[str, None] = None
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ Create Date: 2025-07-06 10:54:19.714809
|
|||||||
|
|
||||||
from typing import Sequence, Union
|
from typing import Sequence, Union
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "aa4689f80796"
|
revision: str = "aa4689f80796"
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
"""add_indexer_column_to_indexerqueryresult
|
||||||
|
|
||||||
|
Revision ID: eb0bd3cc1852
|
||||||
|
Revises: 5299dfed220b
|
||||||
|
Create Date: 2025-10-28 21:39:24.480466
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = "eb0bd3cc1852"
|
||||||
|
down_revision: Union[str, None] = "5299dfed220b"
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column(
|
||||||
|
"indexer_query_result", sa.Column("indexer", sa.String(), nullable=True)
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_column("indexer_query_result", "indexer")
|
||||||
|
# ### end Alembic commands ###
|
||||||
175
config.dev.toml
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
# MediaManager Dev Configuration File
|
||||||
|
# This file contains all available configuration options for MediaManager
|
||||||
|
# Documentation: https://maxdorninger.github.io/MediaManager/
|
||||||
|
#
|
||||||
|
# This is an example configuration file that gets copied to your config folder
|
||||||
|
# on first boot. You should modify the values below to match your setup.
|
||||||
|
|
||||||
|
[misc]
|
||||||
|
# it's very likely that you need to change this for MediaManager to work
|
||||||
|
frontend_url = "http://localhost:5173" # note the lack of a trailing slash
|
||||||
|
cors_urls = ["http://localhost:8000", "http://localhost:5173", "http://mediamanager:8000"] # note the lack of a trailing slash
|
||||||
|
|
||||||
|
image_directory = "/data/images"
|
||||||
|
tv_directory = "/data/tv"
|
||||||
|
movie_directory = "/data/movies"
|
||||||
|
torrent_directory = "/data/torrents" # this is where MediaManager will search for the downloaded torrents and usenet files
|
||||||
|
|
||||||
|
# you probaly don't need to change this
|
||||||
|
development = true
|
||||||
|
|
||||||
|
# Custom Media Libraries
|
||||||
|
# These paths should match your volume mounts in docker-compose.yaml
|
||||||
|
# Example: if you mount "./movies:/media/movies" then use path = "/media/movies/subdirectory"
|
||||||
|
[[misc.tv_libraries]]
|
||||||
|
name = "Live Action"
|
||||||
|
path = "/data/tv/live-action" # Change this to match your actual TV shows location
|
||||||
|
|
||||||
|
[[misc.movie_libraries]]
|
||||||
|
name = "Documentary"
|
||||||
|
path = "/data/movies/documentary" # Change this to match your actual movies location
|
||||||
|
|
||||||
|
[database]
|
||||||
|
host = "db"
|
||||||
|
port = 5432
|
||||||
|
user = "MediaManager"
|
||||||
|
password = "MediaManager"
|
||||||
|
dbname = "MediaManager"
|
||||||
|
|
||||||
|
[auth]
|
||||||
|
email_password_resets = false # if true, you also need to set up SMTP (notifications.smtp_config)
|
||||||
|
|
||||||
|
token_secret = "CHANGE_ME_GENERATE_RANDOM_STRING" # generate a random string with "openssl rand -hex 32", e.g. here https://www.cryptool.org/en/cto/openssl/
|
||||||
|
session_lifetime = 86400 # this is how long you will be logged in after loggin in, in seconds
|
||||||
|
|
||||||
|
# Admin users: Users who register with these email addresses will automatically become administrators
|
||||||
|
# If no users exist in the database, a default admin user will be created with the first email in this list
|
||||||
|
admin_emails = ["admin@example.com", "admin2@example.com"]
|
||||||
|
|
||||||
|
# OpenID Connect settings
|
||||||
|
[auth.openid_connect]
|
||||||
|
enabled = false
|
||||||
|
client_id = ""
|
||||||
|
client_secret = ""
|
||||||
|
configuration_endpoint = "https://openid.example.com/.well-known/openid-configuration"
|
||||||
|
name = "OpenID"
|
||||||
|
|
||||||
|
[notifications]
|
||||||
|
# SMTP settings for email notifications and email password resets
|
||||||
|
[notifications.smtp_config]
|
||||||
|
smtp_host = "smtp.example.com"
|
||||||
|
smtp_port = 587
|
||||||
|
smtp_user = "admin"
|
||||||
|
smtp_password = "admin"
|
||||||
|
from_email = "mediamanager@example.com"
|
||||||
|
use_tls = true
|
||||||
|
|
||||||
|
# Email notification settings
|
||||||
|
[notifications.email_notifications]
|
||||||
|
enabled = false
|
||||||
|
emails = ["admin@example.com", "admin2@example.com"] # List of email addresses to send notifications to
|
||||||
|
|
||||||
|
# Gotify notification settings
|
||||||
|
[notifications.gotify]
|
||||||
|
enabled = false
|
||||||
|
api_key = ""
|
||||||
|
url = "https://gotify.example.com"
|
||||||
|
|
||||||
|
# Ntfy notification settings
|
||||||
|
[notifications.ntfy]
|
||||||
|
enabled = false
|
||||||
|
url = "https://ntfy.sh/your-topic"
|
||||||
|
|
||||||
|
# Pushover notification settings
|
||||||
|
[notifications.pushover]
|
||||||
|
enabled = false
|
||||||
|
api_key = ""
|
||||||
|
user = ""
|
||||||
|
|
||||||
|
[torrents]
|
||||||
|
# qBittorrent settings
|
||||||
|
[torrents.qbittorrent]
|
||||||
|
enabled = false
|
||||||
|
host = "http://localhost"
|
||||||
|
port = 8080
|
||||||
|
username = "admin"
|
||||||
|
password = "admin"
|
||||||
|
|
||||||
|
# Transmission settings
|
||||||
|
[torrents.transmission]
|
||||||
|
enabled = false
|
||||||
|
username = "admin"
|
||||||
|
password = "admin"
|
||||||
|
https_enabled = true
|
||||||
|
host = "localhost"
|
||||||
|
port = 9091
|
||||||
|
path = "/transmission/rpc" # RPC request path target, usually "/transmission/rpc"
|
||||||
|
|
||||||
|
# SABnzbd settings
|
||||||
|
[torrents.sabnzbd]
|
||||||
|
enabled = false
|
||||||
|
host = "http://localhost"
|
||||||
|
port = 8080
|
||||||
|
api_key = ""
|
||||||
|
base_path = "/api"
|
||||||
|
|
||||||
|
[indexers]
|
||||||
|
# Prowlarr settings
|
||||||
|
[indexers.prowlarr]
|
||||||
|
enabled = false
|
||||||
|
url = "http://localhost:9696"
|
||||||
|
api_key = ""
|
||||||
|
timeout_seconds = 60
|
||||||
|
|
||||||
|
# Jackett settings
|
||||||
|
[indexers.jackett]
|
||||||
|
enabled = false
|
||||||
|
url = "http://localhost:9117"
|
||||||
|
api_key = ""
|
||||||
|
indexers = ["1337x", "torrentleech"] # List of indexer names to use
|
||||||
|
timeout_seconds = 60
|
||||||
|
|
||||||
|
# Title-based scoring rules
|
||||||
|
[[indexers.title_scoring_rules]]
|
||||||
|
name = "prefer_h265"
|
||||||
|
keywords = ["h265", "hevc", "x265", "h.265", "x.265"]
|
||||||
|
score_modifier = 100
|
||||||
|
negate = false
|
||||||
|
|
||||||
|
[[indexers.title_scoring_rules]]
|
||||||
|
name = "avoid_cam"
|
||||||
|
keywords = ["cam", "ts"]
|
||||||
|
score_modifier = -10000
|
||||||
|
negate = false
|
||||||
|
|
||||||
|
# Indexer flag-based scoring rules
|
||||||
|
[[indexers.indexer_flag_scoring_rules]]
|
||||||
|
name = "reject_non_freeleech"
|
||||||
|
flags = ["freeleech", "freeleech75"]
|
||||||
|
score_modifier = -10000
|
||||||
|
negate = true
|
||||||
|
|
||||||
|
[[indexers.indexer_flag_scoring_rules]]
|
||||||
|
name = "reject_nuked"
|
||||||
|
flags = ["nuked"]
|
||||||
|
score_modifier = -10000
|
||||||
|
negate = false
|
||||||
|
|
||||||
|
# Scoring rulesets
|
||||||
|
[[indexers.scoring_rule_sets]]
|
||||||
|
name = "default"
|
||||||
|
libraries = ["ALL_TV", "ALL_MOVIES"]
|
||||||
|
rule_names = ["prefer_h265", "avoid_cam", "reject_nuked"]
|
||||||
|
|
||||||
|
[[indexers.scoring_rule_sets]]
|
||||||
|
name = "strict_quality"
|
||||||
|
libraries = ["ALL_MOVIES"]
|
||||||
|
rule_names = ["prefer_h265", "avoid_cam", "reject_non_freeleech"]
|
||||||
|
|
||||||
|
# its very unlikely that you need to change this
|
||||||
|
[metadata]
|
||||||
|
[metadata.tmdb]
|
||||||
|
tmdb_relay_url = "https://metadata-relay.dorninger.co/tmdb"
|
||||||
|
|
||||||
|
[metadata.tvdb]
|
||||||
|
tvdb_relay_url = "https://metadata-relay.dorninger.co/tvdb"
|
||||||
172
config.example.toml
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
# MediaManager Example Configuration File
|
||||||
|
# This file contains all available configuration options for MediaManager
|
||||||
|
# Documentation: https://maxdorninger.github.io/MediaManager/
|
||||||
|
#
|
||||||
|
# This is an example configuration file that gets copied to your config folder
|
||||||
|
# on first boot. You should modify the values below to match your setup.
|
||||||
|
|
||||||
|
[misc]
|
||||||
|
# it's very likely that you need to change this for MediaManager to work
|
||||||
|
frontend_url = "http://localhost:8000" # note the lack of a trailing slash
|
||||||
|
cors_urls = ["http://localhost:8000"] # note the lack of a trailing slash
|
||||||
|
|
||||||
|
image_directory = "/data/images"
|
||||||
|
tv_directory = "/data/tv"
|
||||||
|
movie_directory = "/data/movies"
|
||||||
|
torrent_directory = "/data/torrents" # this is where MediaManager will search for the downloaded torrents and usenet files
|
||||||
|
|
||||||
|
# you probaly don't need to change this
|
||||||
|
development = false
|
||||||
|
|
||||||
|
# Custom Media Libraries
|
||||||
|
# These paths should match your volume mounts in docker-compose.yaml
|
||||||
|
# Example: if you mount "./movies:/media/movies" then use path = "/media/movies/subdirectory"
|
||||||
|
[[misc.tv_libraries]]
|
||||||
|
name = "Live Action"
|
||||||
|
path = "/data/tv/live-action" # Change this to match your actual TV shows location
|
||||||
|
|
||||||
|
[[misc.movie_libraries]]
|
||||||
|
name = "Documentary"
|
||||||
|
path = "/data/movies/documentary" # Change this to match your actual movies location
|
||||||
|
|
||||||
|
[database]
|
||||||
|
host = "db"
|
||||||
|
port = 5432
|
||||||
|
user = "MediaManager"
|
||||||
|
password = "MediaManager"
|
||||||
|
dbname = "MediaManager"
|
||||||
|
|
||||||
|
[auth]
|
||||||
|
email_password_resets = false # if true, you also need to set up SMTP (notifications.smtp_config)
|
||||||
|
|
||||||
|
token_secret = "CHANGE_ME_GENERATE_RANDOM_STRING" # generate a random string with "openssl rand -hex 32", e.g. here https://www.cryptool.org/en/cto/openssl/
|
||||||
|
session_lifetime = 86400 # this is how long you will be logged in after loggin in, in seconds
|
||||||
|
|
||||||
|
# Admin users: Users who register with these email addresses will automatically become administrators
|
||||||
|
# If no users exist in the database, a default admin user will be created with the first email in this list
|
||||||
|
admin_emails = ["admin@example.com", "admin2@example.com"]
|
||||||
|
|
||||||
|
# OpenID Connect settings
|
||||||
|
[auth.openid_connect]
|
||||||
|
enabled = false
|
||||||
|
client_id = ""
|
||||||
|
client_secret = ""
|
||||||
|
configuration_endpoint = "https://openid.example.com/.well-known/openid-configuration"
|
||||||
|
name = "OpenID"
|
||||||
|
|
||||||
|
[notifications]
|
||||||
|
# SMTP settings for email notifications and email password resets
|
||||||
|
[notifications.smtp_config]
|
||||||
|
smtp_host = "smtp.example.com"
|
||||||
|
smtp_port = 587
|
||||||
|
smtp_user = "admin"
|
||||||
|
smtp_password = "admin"
|
||||||
|
from_email = "mediamanager@example.com"
|
||||||
|
use_tls = true
|
||||||
|
|
||||||
|
# Email notification settings
|
||||||
|
[notifications.email_notifications]
|
||||||
|
enabled = false
|
||||||
|
emails = ["admin@example.com", "admin2@example.com"] # List of email addresses to send notifications to
|
||||||
|
|
||||||
|
# Gotify notification settings
|
||||||
|
[notifications.gotify]
|
||||||
|
enabled = false
|
||||||
|
api_key = ""
|
||||||
|
url = "https://gotify.example.com"
|
||||||
|
|
||||||
|
# Ntfy notification settings
|
||||||
|
[notifications.ntfy]
|
||||||
|
enabled = false
|
||||||
|
url = "https://ntfy.sh/your-topic"
|
||||||
|
|
||||||
|
# Pushover notification settings
|
||||||
|
[notifications.pushover]
|
||||||
|
enabled = false
|
||||||
|
api_key = ""
|
||||||
|
user = ""
|
||||||
|
|
||||||
|
[torrents]
|
||||||
|
# qBittorrent settings
|
||||||
|
[torrents.qbittorrent]
|
||||||
|
enabled = false
|
||||||
|
host = "http://localhost"
|
||||||
|
port = 8080
|
||||||
|
username = "admin"
|
||||||
|
password = "admin"
|
||||||
|
|
||||||
|
# Transmission settings
|
||||||
|
[torrents.transmission]
|
||||||
|
enabled = false
|
||||||
|
username = "admin"
|
||||||
|
password = "admin"
|
||||||
|
https_enabled = true
|
||||||
|
host = "localhost"
|
||||||
|
port = 9091
|
||||||
|
path = "/transmission/rpc" # RPC request path target, usually "/transmission/rpc"
|
||||||
|
|
||||||
|
# SABnzbd settings
|
||||||
|
[torrents.sabnzbd]
|
||||||
|
enabled = false
|
||||||
|
host = "http://localhost"
|
||||||
|
port = 8080
|
||||||
|
api_key = ""
|
||||||
|
base_path = "/api"
|
||||||
|
|
||||||
|
[indexers]
|
||||||
|
# Prowlarr settings
|
||||||
|
[indexers.prowlarr]
|
||||||
|
enabled = false
|
||||||
|
url = "http://localhost:9696"
|
||||||
|
api_key = ""
|
||||||
|
timeout_seconds = 60
|
||||||
|
|
||||||
|
# Jackett settings
|
||||||
|
[indexers.jackett]
|
||||||
|
enabled = false
|
||||||
|
url = "http://localhost:9117"
|
||||||
|
api_key = ""
|
||||||
|
indexers = ["1337x", "torrentleech"] # List of indexer names to use
|
||||||
|
timeout_seconds = 60
|
||||||
|
|
||||||
|
# Title-based scoring rules
|
||||||
|
[[indexers.title_scoring_rules]]
|
||||||
|
name = "prefer_h265"
|
||||||
|
keywords = ["h265", "hevc", "x265", "h.265", "x.265"]
|
||||||
|
score_modifier = 100
|
||||||
|
negate = false
|
||||||
|
|
||||||
|
[[indexers.title_scoring_rules]]
|
||||||
|
name = "avoid_cam"
|
||||||
|
keywords = ["cam", "ts"]
|
||||||
|
score_modifier = -10000
|
||||||
|
negate = false
|
||||||
|
|
||||||
|
# Indexer flag-based scoring rules
|
||||||
|
[[indexers.indexer_flag_scoring_rules]]
|
||||||
|
name = "prefer_freeleech"
|
||||||
|
flags = ["freeleech", "freeleech75"]
|
||||||
|
score_modifier = 100
|
||||||
|
negate = false
|
||||||
|
|
||||||
|
[[indexers.indexer_flag_scoring_rules]]
|
||||||
|
name = "reject_nuked"
|
||||||
|
flags = ["nuked"]
|
||||||
|
score_modifier = -10000
|
||||||
|
negate = false
|
||||||
|
|
||||||
|
# Scoring rulesets
|
||||||
|
[[indexers.scoring_rule_sets]]
|
||||||
|
name = "default"
|
||||||
|
libraries = ["ALL_TV", "ALL_MOVIES"]
|
||||||
|
rule_names = ["prefer_h265", "avoid_cam", "reject_nuked", "prefer_freeleech"]
|
||||||
|
|
||||||
|
# its very unlikely that you need to change this
|
||||||
|
[metadata]
|
||||||
|
[metadata.tmdb]
|
||||||
|
tmdb_relay_url = "https://metadata-relay.dorninger.co/tmdb"
|
||||||
|
primary_languages = [""]
|
||||||
|
default_language = "en"
|
||||||
|
|
||||||
|
[metadata.tvdb]
|
||||||
|
tvdb_relay_url = "https://metadata-relay.dorninger.co/tvdb"
|
||||||
114
config.toml
@@ -1,114 +0,0 @@
|
|||||||
# MediaManager Complete Configuration File
|
|
||||||
# This file contains all available configuration options for MediaManager
|
|
||||||
# Documentation: https://maxdorninger.github.io/MediaManager/introduction.html
|
|
||||||
|
|
||||||
[misc]
|
|
||||||
# it's very likely that you need to change this for MediaManager to work
|
|
||||||
frontend_url = "http://localhost:3000/" # note the trailing slash
|
|
||||||
cors_urls = ["http://localhost:3000", "http://localhost:8000"] # note the lack of a trailing slash
|
|
||||||
|
|
||||||
# you probaly don't need to change this
|
|
||||||
api_base_path = "/api/v1"
|
|
||||||
development = false
|
|
||||||
|
|
||||||
[database]
|
|
||||||
host = "db"
|
|
||||||
port = 5432
|
|
||||||
user = "MediaManager"
|
|
||||||
password = "MediaManager"
|
|
||||||
dbname = "MediaManager"
|
|
||||||
|
|
||||||
[auth]
|
|
||||||
email_password_resets = false # if true, you also need to set up SMTP (notifications.smtp_config)
|
|
||||||
|
|
||||||
token_secret = "" # generate a random string with "openssl rand -hex 32", e.g. here https://www.cryptool.org/en/cto/openssl/
|
|
||||||
session_lifetime = 86400 # this is how long you will be logged in after loggin in, in seconds
|
|
||||||
admin_emails = ["admin@example.com", "admin2@example.com"]
|
|
||||||
|
|
||||||
# OpenID Connect settings
|
|
||||||
[auth.openid_connect]
|
|
||||||
enabled = false
|
|
||||||
client_id = ""
|
|
||||||
client_secret = ""
|
|
||||||
configuration_endpoint = "https://openid.example.com/.well-known/openid-configuration"
|
|
||||||
name = "OpenID"
|
|
||||||
|
|
||||||
[notifications]
|
|
||||||
# SMTP settings for email notifications and email password resets
|
|
||||||
[notifications.smtp_config]
|
|
||||||
smtp_host = "smtp.example.com"
|
|
||||||
smtp_port = 587
|
|
||||||
smtp_user = "admin"
|
|
||||||
smtp_password = "admin"
|
|
||||||
from_email = "mediamanager@example.com"
|
|
||||||
use_tls = true
|
|
||||||
|
|
||||||
# Email notification settings
|
|
||||||
[notifications.email_notifications]
|
|
||||||
enabled = false
|
|
||||||
emails = ["admin@example.com", "admin2@example.com"] # List of email addresses to send notifications to
|
|
||||||
|
|
||||||
# Gotify notification settings
|
|
||||||
[notifications.gotify]
|
|
||||||
enabled = false
|
|
||||||
api_key = ""
|
|
||||||
url = "https://gotify.example.com"
|
|
||||||
|
|
||||||
# Ntfy notification settings
|
|
||||||
[notifications.ntfy]
|
|
||||||
enabled = false
|
|
||||||
url = "https://ntfy.sh/your-topic"
|
|
||||||
|
|
||||||
# Pushover notification settings
|
|
||||||
[notifications.pushover]
|
|
||||||
enabled = false
|
|
||||||
api_key = ""
|
|
||||||
user = ""
|
|
||||||
|
|
||||||
[torrents]
|
|
||||||
# qBittorrent settings
|
|
||||||
[torrents.qbittorrent]
|
|
||||||
enabled = false
|
|
||||||
host = "http://localhost"
|
|
||||||
port = 8080
|
|
||||||
username = "admin"
|
|
||||||
password = "admin"
|
|
||||||
|
|
||||||
# Transmission settings
|
|
||||||
[torrents.transmission]
|
|
||||||
enabled = false
|
|
||||||
username = "admin"
|
|
||||||
password = "admin"
|
|
||||||
https_enabled = true
|
|
||||||
host = "localhost"
|
|
||||||
port = 9091
|
|
||||||
path = "/transmission/rpc" # RPC request path target, usually "/transmission/rpc"
|
|
||||||
|
|
||||||
# SABnzbd settings
|
|
||||||
[torrents.sabnzbd]
|
|
||||||
enabled = false
|
|
||||||
host = "localhost"
|
|
||||||
port = 8080
|
|
||||||
api_key = ""
|
|
||||||
|
|
||||||
[indexers]
|
|
||||||
# Prowlarr settings
|
|
||||||
[indexers.prowlarr]
|
|
||||||
enabled = false
|
|
||||||
url = "http://localhost:9696"
|
|
||||||
api_key = ""
|
|
||||||
|
|
||||||
# Jackett settings
|
|
||||||
[indexers.jackett]
|
|
||||||
enabled = false
|
|
||||||
url = "http://localhost:9117"
|
|
||||||
api_key = ""
|
|
||||||
indexers = ["1337x"] # List of indexer names to use
|
|
||||||
|
|
||||||
# its very unlikely that you need to change this
|
|
||||||
[metadata]
|
|
||||||
[metadata.tmdb]
|
|
||||||
tmdb_relay_url = "https://metadata-relay.maxid.me/tmdb"
|
|
||||||
|
|
||||||
[metadata.tvdb]
|
|
||||||
tvdb_relay_url = "https://metadata-relay.maxid.me/tvdb"
|
|
||||||
155
docker-compose.dev.yaml
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: postgres:17
|
||||||
|
restart: unless-stopped
|
||||||
|
container_name: postgres
|
||||||
|
volumes:
|
||||||
|
- ./res/postgres:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: MediaManager
|
||||||
|
POSTGRES_DB: MediaManager
|
||||||
|
POSTGRES_PASSWORD: MediaManager
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}" ]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
mediamanager:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
target: app
|
||||||
|
args:
|
||||||
|
- VERSION=locally-built
|
||||||
|
- BASE_PATH=
|
||||||
|
container_name: mediamanager
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
environment:
|
||||||
|
- CONFIG_DIR=/app/config
|
||||||
|
- MEDIAMANAGER_MISC__DEVELOPMENT=TRUE
|
||||||
|
- MEDIAMANAGER_MISC__CORS_URLS=["http://localhost:5173"]
|
||||||
|
- DISABLE_FRONTEND_MOUNT=TRUE
|
||||||
|
- LOG_FILE=/dev/null
|
||||||
|
- MEDIAMANAGER_LOG_LEVEL=DEBUG
|
||||||
|
volumes:
|
||||||
|
#- ./web/build:/app/web/build # this is only needed to test built frontend when developing frontend
|
||||||
|
- ./res/images/:/data/images/
|
||||||
|
- ./res/:/data/
|
||||||
|
- ./res/config/:/app/config/
|
||||||
|
- ./media_manager:/app/media_manager
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
frontend:
|
||||||
|
image: node:24-alpine
|
||||||
|
container_name: mediamanager-frontend-dev
|
||||||
|
working_dir: /app
|
||||||
|
command: sh -c "npm install && npm run dev -- --host 0.0.0.0"
|
||||||
|
ports:
|
||||||
|
- "5173:5173"
|
||||||
|
- "24678:24678"
|
||||||
|
volumes:
|
||||||
|
- ./web:/app
|
||||||
|
depends_on:
|
||||||
|
- mediamanager
|
||||||
|
docs:
|
||||||
|
image: squidfunk/mkdocs-material:9
|
||||||
|
container_name: mediamanager-docs
|
||||||
|
volumes:
|
||||||
|
- .:/docs
|
||||||
|
ports:
|
||||||
|
- "9000:9000"
|
||||||
|
command: serve -w /docs -a 0.0.0.0:9000
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------
|
||||||
|
# Additional services can be uncommented and configured as needed
|
||||||
|
# ----------------------------
|
||||||
|
|
||||||
|
prowlarr:
|
||||||
|
image: lscr.io/linuxserver/prowlarr:latest
|
||||||
|
container_name: prowlarr
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Etc/UTC
|
||||||
|
volumes:
|
||||||
|
- ./res/prowlarr:/config
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "9696:9696"
|
||||||
|
qbittorrent:
|
||||||
|
image: lscr.io/linuxserver/qbittorrent:latest
|
||||||
|
container_name: qbittorrent
|
||||||
|
environment:
|
||||||
|
- TZ=Etc/UTC
|
||||||
|
- WEBUI_PORT=8080
|
||||||
|
- TORRENTING_PORT=6881
|
||||||
|
ports:
|
||||||
|
- 8080:8080
|
||||||
|
- 6881:6881
|
||||||
|
- 6881:6881/udp
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./res/torrents:/download
|
||||||
|
- ./res/qbittorrent:/config
|
||||||
|
# transmission:
|
||||||
|
# image: lscr.io/linuxserver/transmission:latest
|
||||||
|
# container_name: transmission
|
||||||
|
# environment:
|
||||||
|
# - PUID=1000
|
||||||
|
# - PGID=1000
|
||||||
|
# - TZ=Etc/UTC
|
||||||
|
# - USER=admin
|
||||||
|
# - PASS=admin
|
||||||
|
# volumes:
|
||||||
|
# - ./res/transmission:/config
|
||||||
|
# - ./res/torrents:/data/torrents
|
||||||
|
# ports:
|
||||||
|
# - 9091:9091
|
||||||
|
# restart: unless-stopped
|
||||||
|
# pocket-id:
|
||||||
|
# image: ghcr.io/pocket-id/pocket-id
|
||||||
|
# restart: unless-stopped
|
||||||
|
# env_file: .env
|
||||||
|
# ports:
|
||||||
|
# - 1411:1411
|
||||||
|
# volumes:
|
||||||
|
# - ./res/pocket-id:/app/data
|
||||||
|
# healthcheck:
|
||||||
|
# test: "curl -f http://localhost:1411/healthz"
|
||||||
|
# interval: 1m30s
|
||||||
|
# timeout: 5s
|
||||||
|
# retries: 2
|
||||||
|
# start_period: 10s
|
||||||
|
# sabnzbd:
|
||||||
|
# image: lscr.io/linuxserver/sabnzbd:latest
|
||||||
|
# container_name: sabnzbd
|
||||||
|
# environment:
|
||||||
|
# - PUID=1000
|
||||||
|
# - PGID=1000
|
||||||
|
# - TZ=Etc/UTC
|
||||||
|
# volumes:
|
||||||
|
# - ./res/sabnzbd:/config
|
||||||
|
# - ./res/torrents:/downloads
|
||||||
|
# ports:
|
||||||
|
# - 8081:8080
|
||||||
|
# restart: unless-stopped
|
||||||
|
jackett:
|
||||||
|
image: lscr.io/linuxserver/jackett:latest
|
||||||
|
container_name: jackett
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Etc/UTC
|
||||||
|
- AUTO_UPDATE=true
|
||||||
|
volumes:
|
||||||
|
- ./res/jackett/data:/config
|
||||||
|
- ./res/jackett/torrents:/downloads
|
||||||
|
ports:
|
||||||
|
- 9117:9117
|
||||||
|
restart: unless-stopped
|
||||||
@@ -1,26 +1,29 @@
|
|||||||
services:
|
services:
|
||||||
backend:
|
mediamanager:
|
||||||
image: ghcr.io/maxdorninger/mediamanager/backend:latest
|
container_name: mediamanager_server
|
||||||
|
image: quay.io/maxdorninger/mediamanager:latest
|
||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
environment:
|
environment:
|
||||||
- CONFIG_FILE=/app/config.toml
|
- CONFIG_DIR=/app/config
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/:/data/
|
# Mount your actual media directories here - these paths should match your config.toml
|
||||||
- ./config.toml:/app/config.toml
|
- ./data/:/data/ # Example: change ./data/ to your actual media root
|
||||||
frontend:
|
# Config folder for application configuration
|
||||||
image: ghcr.io/maxdorninger/mediamanager/frontend:latest
|
- ./config/:/app/config/
|
||||||
ports:
|
# Image folder
|
||||||
- "3000:3000"
|
- ./images/:/data/images/
|
||||||
environment:
|
depends_on:
|
||||||
- PUBLIC_API_URL=http://localhost:8000/api/v1
|
|
||||||
db:
|
db:
|
||||||
image: postgres:latest
|
condition: service_healthy
|
||||||
|
db:
|
||||||
|
container_name: mediamanager_postgres
|
||||||
|
image: postgres:17
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- ./postgres:/var/lib/postgresql/data
|
- ./postgres:/var/lib/postgresql/data
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: [ "CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
|
test: [ "CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}" ]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
@@ -28,7 +31,3 @@ services:
|
|||||||
POSTGRES_USER: MediaManager
|
POSTGRES_USER: MediaManager
|
||||||
POSTGRES_DB: MediaManager
|
POSTGRES_DB: MediaManager
|
||||||
POSTGRES_PASSWORD: MediaManager
|
POSTGRES_PASSWORD: MediaManager
|
||||||
ports:
|
|
||||||
- "5432:5432"
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
4
docs/advanced-features/custom-port.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Custom port
|
||||||
|
|
||||||
|
* `PORT`\
|
||||||
|
Port that MediaManager listens on. Default is `8000`. This only works if you are using the Docker image. Configured as environment variable.
|
||||||
4
docs/advanced-features/disable-startup-ascii-art.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Disable Startup Ascii Art
|
||||||
|
|
||||||
|
* `MEDIAMANAGER_NO_STARTUP_ART`: Set this environment variable (to any value) \
|
||||||
|
to disable the colorized startup splash screen. Unset to reenable.
|
||||||
12
docs/advanced-features/follow-symlinks-in-frontend-files.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Follow symlinks in frontend files
|
||||||
|
|
||||||
|
MediaManager can be configured to follow symlinks when serving frontend files. This is useful if you have a setup where your frontend files are stored in a different location, and you want to symlink them into the MediaManager frontend directory.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* `FRONTEND_FOLLOW_SYMLINKS`\
|
||||||
|
Set this environment variable to `true` to follow symlinks when serving frontend files. Default is `false`.
|
||||||
|
|
||||||
|
```bash title=".env"
|
||||||
|
FRONTEND_FOLLOW_SYMLINKS=true
|
||||||
|
```
|
||||||
65
docs/advanced-features/metadata-provider-configuration.md
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# Metadata Provider Configuration
|
||||||
|
|
||||||
|
## Metadata Provider Configuration
|
||||||
|
|
||||||
|
Metadata provider settings are configured in the `[metadata]` section of your `config.toml` file. These settings control how MediaManager retrieves information about movies and TV shows.
|
||||||
|
|
||||||
|
### TMDB Settings (`[metadata.tmdb]`)
|
||||||
|
|
||||||
|
TMDB (The Movie Database) is the primary metadata provider for MediaManager. It provides detailed information about movies and TV shows.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
Other software like Jellyfin use TMDB as well, so there won't be any metadata discrepancies.
|
||||||
|
|
||||||
|
* `tmdb_relay_url`\
|
||||||
|
URL of the TMDB relay (MetadataRelay). Default is `https://metadata-relay.dorninger.co/tmdb`. Example: `https://your-own-relay.example.com/tmdb`.
|
||||||
|
* `primary_languages`\
|
||||||
|
If the original language of a show/movie is in this list, metadata is fetched in that language. Otherwise, `default_language` is used. Default is `[]`. Example: `["no", "de", "es"]`. Format: ISO 639-1 (2 letters). Full list: https://en.wikipedia.org/wiki/List\_of\_ISO\_639\_language\_codes
|
||||||
|
* `default_language`\
|
||||||
|
TMDB language parameter used when searching and adding. Default is `en`. Format: ISO 639-1 (2 letters).
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
`default_language` sets the TMDB `language` parameter when searching and adding TV shows and movies. If TMDB does not find a matching translation, metadata in the original language will be fetched with no option for a fallback language. It is therefore highly advised to only use "broad" languages. For most use cases, the default setting is safest.
|
||||||
|
|
||||||
|
### TVDB Settings (`[metadata.tvdb]`)
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
The TVDB might provide false metadata and doesn't support some features of MediaManager like showing overviews. Therefore, TMDB is the preferred metadata provider.
|
||||||
|
|
||||||
|
* `tvdb_relay_url`\
|
||||||
|
URL of the TVDB relay (MetadataRelay). Default is `https://metadata-relay.dorninger.co/tvdb`. Example: `https://your-own-relay.example.com/tvdb`.
|
||||||
|
|
||||||
|
### MetadataRelay
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
To use MediaManager you don't need to set up your own MetadataRelay, as the default relay hosted by the developer should be sufficient for most purposes.
|
||||||
|
|
||||||
|
The MetadataRelay is a service that provides metadata for MediaManager. It acts as a proxy for TMDB and TVDB, allowing you to use your own API keys if needed, but the default relay means you don't need to create accounts for API keys yourself.
|
||||||
|
|
||||||
|
You might want to use your own relay if you want to avoid rate limits, protect your privacy, or for other reasons. If you know Sonarr's Skyhook, this is similar to that.
|
||||||
|
|
||||||
|
#### Where to get API keys
|
||||||
|
|
||||||
|
* Get a TMDB API key from [The Movie Database](https://www.themoviedb.org/settings/api)
|
||||||
|
* Get a TVDB API key from [The TVDB](https://thetvdb.com/auth/register)
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
If you want to use your own MetadataRelay, you can set the `tmdb_relay_url` and/or `tvdb_relay_url` to your own relay service.
|
||||||
|
|
||||||
|
### Example Configuration
|
||||||
|
|
||||||
|
Here's a complete example of the metadata section in your `config.toml`:
|
||||||
|
|
||||||
|
```toml title="config.toml"
|
||||||
|
[metadata]
|
||||||
|
# TMDB configuration
|
||||||
|
[metadata.tmdb]
|
||||||
|
tmdb_relay_url = "https://metadata-relay.dorninger.co/tmdb"
|
||||||
|
|
||||||
|
# TVDB configuration
|
||||||
|
[metadata.tvdb]
|
||||||
|
tvdb_relay_url = "https://metadata-relay.dorninger.co/tvdb"
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
In most cases, you can simply use the default values and don't need to specify these settings in your config file at all.
|
||||||
16
docs/advanced-features/qbittorrent-category.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# qBittorrent Category
|
||||||
|
|
||||||
|
qBittorrent supports saving Torrents to subdirectories based on the category of the Torrent. The default category name that MediaManager uses is `MediaManager`.
|
||||||
|
|
||||||
|
Use the following variables to customize behavior:
|
||||||
|
|
||||||
|
* `torrents.qbittorrent.category_name`\
|
||||||
|
Category name MediaManager uses when adding torrents to qBittorrent. Default is `MediaManager`.
|
||||||
|
* `torrents.qbittorrent.category_save_path`\
|
||||||
|
Save path for the category in qBittorrent. By default, no subdirectory is used. Example: `/data/torrents/MediaManager`.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
qBittorrent saves torrents to the path specified by `torrents.qbittorrent.category_save_path`, so it must be a valid path that qBittorrent can write to.
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
For MediaManager to successfully import torrents, you must add the subdirectory to the `misc.torrent_directory` variable.
|
||||||
36
docs/advanced-features/url-prefix.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# URL Prefix
|
||||||
|
|
||||||
|
MediaManager, by default, expects to run at the base of a domain, e.g. `maxdorninger.github.io`.
|
||||||
|
|
||||||
|
In order to run it on a prefixed path, like `maxdorninger.github.io/media`, the docker image must be built with a special build argument. That's because SvelteKit needs to know the base URL at build time.
|
||||||
|
|
||||||
|
In short, clone the repository, then run:
|
||||||
|
|
||||||
|
```none title="Build Docker image"
|
||||||
|
docker build \
|
||||||
|
--build-arg BASE_PATH=/media \
|
||||||
|
--build-arg VERSION=my-custom-version \
|
||||||
|
-t MediaManager:my-custom-version \
|
||||||
|
-f Dockerfile .
|
||||||
|
```
|
||||||
|
|
||||||
|
You also need to set the `BASE_PATH` environment variable at runtime in `docker-compose.yaml`:
|
||||||
|
|
||||||
|
* `BASE_PATH`\
|
||||||
|
Base path prefix MediaManager is served under. Example: `/media`. This must match the `BASE_PATH` build arg.
|
||||||
|
|
||||||
|
```yaml title="docker-compose.yaml (excerpt)"
|
||||||
|
services:
|
||||||
|
mediamanager:
|
||||||
|
image: MediaManager:my-custom-version
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
environment:
|
||||||
|
BASE_PATH: /media
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
Make sure to include the base path in the `frontend_url` field in the config file. See [Backend](../configuration/backend.md).
|
||||||
|
|
||||||
|
Finally, ensure that whatever reverse proxy you're using leaves the incoming path unchanged; that is, you should not strip the `/media` from `/media/web/`.
|
||||||
7
docs/api-reference.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# API Reference
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
Media Manager's backend is built with FastAPI, which automatically generates interactive API documentation.
|
||||||
|
|
||||||
|
* Swagger UI (typically available at `http://localhost:8000/docs`)
|
||||||
|
* ReDoc (typically available at `http://localhost:8000/redoc`)
|
||||||
BIN
docs/assets/assets/2VeQ8sTGPhj4tiLy.jpg
Normal file
|
After Width: | Height: | Size: 3.1 MiB |
BIN
docs/assets/assets/5546622
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
docs/assets/assets/HadrienKerlero.png
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
docs/assets/assets/JO.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
docs/assets/assets/LITUATUI.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
docs/assets/assets/NI.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
docs/assets/assets/SeimusS.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
docs/assets/assets/authentik redirect url example.png
Normal file
|
After Width: | Height: | Size: 244 KiB |
BIN
docs/assets/assets/bmc-button.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
docs/assets/assets/bmc-logo-yellow.png
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
docs/assets/assets/brandon dacrib.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
docs/assets/assets/e93602705171fd0a.jpg
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
docs/assets/assets/image.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
docs/assets/assets/keyxmakerx.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
docs/assets/assets/ldrrp.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 8.9 MiB After Width: | Height: | Size: 8.9 MiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 5.5 MiB After Width: | Height: | Size: 5.5 MiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 7.6 MiB After Width: | Height: | Size: 7.6 MiB |
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
69
docs/configuration/README.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# Configuration
|
||||||
|
|
||||||
|
MediaManager uses a TOML configuration file (`config.toml`) for all backend settings. This centralized configuration approach makes it easier to manage, backup, and share your MediaManager setup.
|
||||||
|
|
||||||
|
Frontend settings are configured through environment variables in your `docker-compose.yaml` file.
|
||||||
|
|
||||||
|
## Configuration File Location
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
Note that MediaManager may need to be restarted for changes in the config file to take effect.
|
||||||
|
|
||||||
|
Your `config.toml` file should be in the directory that's mounted to `/app/config/config.toml` inside the container:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
- ./config:/app/config
|
||||||
|
```
|
||||||
|
|
||||||
|
You can change the configuration directory with the following environment variable:
|
||||||
|
|
||||||
|
* `CONFIG_DIR`\
|
||||||
|
Directory that contains `config.toml`. Default is `/app/config`. Example: `/etc/mediamanager/`.
|
||||||
|
|
||||||
|
## Configuration Sections
|
||||||
|
|
||||||
|
The configuration is organized into the following sections:
|
||||||
|
|
||||||
|
* `[misc]` - General settings
|
||||||
|
* `[database]` - Database settings
|
||||||
|
* `[auth]` - Authentication settings
|
||||||
|
* `[notifications]` - Notification settings (Email, Gotify, Ntfy, Pushover)
|
||||||
|
* `[torrents]` - Download client settings (qBittorrent, Transmission, SABnzbd)
|
||||||
|
* `[indexers]` - Indexer settings (Prowlarr and Jackett )
|
||||||
|
* `[metadata]` - TMDB and TVDB settings
|
||||||
|
|
||||||
|
## Configuring Secrets
|
||||||
|
|
||||||
|
For sensitive information like API keys, passwords, and secrets, you should use environment variables. You can actually set every configuration value through environment variables. For example, to set the `token_secret` value for authentication, with a .toml file you would use:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[auth]
|
||||||
|
token_secret = "your_super_secret_key_here"
|
||||||
|
```
|
||||||
|
|
||||||
|
But you can also set it through an environment variable:
|
||||||
|
|
||||||
|
```none
|
||||||
|
MEDIAMANAGER_AUTH__TOKEN_SECRET = "your_super_secret_key_here"
|
||||||
|
```
|
||||||
|
|
||||||
|
or another example with the OIDC client secret:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[auth]
|
||||||
|
...
|
||||||
|
[auth.openid_connect]
|
||||||
|
client_secret = "your_client_secret_from_provider"
|
||||||
|
```
|
||||||
|
|
||||||
|
env variable:
|
||||||
|
|
||||||
|
```none
|
||||||
|
MEDIAMANAGER_AUTH__OPENID_CONNECT__CLIENT_SECRET = "your_client_secret_from_provider"
|
||||||
|
```
|
||||||
|
|
||||||
|
So for every config "level", you basically have to take the name of the value and prepend it with the section names in uppercase with 2 underscores as delimiters and `MEDIAMANAGER_` as the prefix.
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
Note that not every env variable starts with `MEDIAMANAGER_`; this prefix only applies to env variables which replace/overwrite values in the config file. Variables like the `CONFIG_DIR` env variable must not be prefixed.
|
||||||
81
docs/configuration/authentication.md
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
---
|
||||||
|
description: >-
|
||||||
|
MediaManager supports multiple authentication methods. Email/password
|
||||||
|
authentication is the default, but you can also enable OpenID Connect (OAuth
|
||||||
|
2.0) for integration with external identity providers
|
||||||
|
---
|
||||||
|
|
||||||
|
# Authentication
|
||||||
|
|
||||||
|
All authentication settings are configured in the `[auth]` section of your `config.toml` file.
|
||||||
|
|
||||||
|
## General Authentication Settings (`[auth]`)
|
||||||
|
|
||||||
|
* `token_secret`\
|
||||||
|
Strong secret key for signing JWTs (create with `openssl rand -hex 32`). This is required.
|
||||||
|
* `session_lifetime`\
|
||||||
|
Lifetime of user sessions in seconds. Default is `86400` (1 day).
|
||||||
|
* `admin_emails`\
|
||||||
|
A list of email addresses for administrator accounts. This is required.
|
||||||
|
* `email_password_resets`\
|
||||||
|
Enables password resets via email. Default is `false`.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
To use email password resets, you must also configure SMTP settings in the `[notifications.smtp_config]` section.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
When setting up MediaManager for the first time, you should add your email to `admin_emails` in the `[auth]` config section. MediaManager will then use this email instead of the default admin email. Your account will automatically be created as an admin account, allowing you to manage other users, media and settings.
|
||||||
|
|
||||||
|
## OpenID Connect Settings (`[auth.openid_connect]`)
|
||||||
|
|
||||||
|
OpenID Connect allows you to integrate with external identity providers like Google, Microsoft Azure AD, Keycloak, or any other OIDC-compliant provider.
|
||||||
|
|
||||||
|
* `enabled`\
|
||||||
|
Set to `true` to enable OpenID Connect authentication. Default is `false`.
|
||||||
|
* `client_id`\
|
||||||
|
Client ID provided by your OpenID Connect provider.
|
||||||
|
* `client_secret`\
|
||||||
|
Client secret provided by your OpenID Connect provider.
|
||||||
|
* `configuration_endpoint`\
|
||||||
|
OpenID Connect configuration endpoint URL. Do not include a trailing slash. Usually ends with `/.well-known/openid-configuration`.
|
||||||
|
* `name`\
|
||||||
|
Display name for the OpenID Connect provider shown on the login page.
|
||||||
|
|
||||||
|
### Configuration for your OpenID Connect Provider
|
||||||
|
|
||||||
|
#### Redirect URI
|
||||||
|
|
||||||
|
The OpenID server will likely require a redirect URI. This URL will usually look something like this:
|
||||||
|
|
||||||
|
```none
|
||||||
|
{MEDIAMANAGER_URL}/api/v1/auth/oauth/callback
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
It is very important that you set the correct callback URI, otherwise it won't work!
|
||||||
|
|
||||||
|
#### Authentik Example
|
||||||
|
|
||||||
|
Here is an example configuration for the OpenID Connect provider for Authentik.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Example Configuration
|
||||||
|
|
||||||
|
Here's a complete example of the authentication section in your `config.toml`:
|
||||||
|
|
||||||
|
```toml title="config.toml"
|
||||||
|
[auth]
|
||||||
|
token_secret = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6"
|
||||||
|
session_lifetime = 604800 # 1 week
|
||||||
|
admin_emails = ["admin@example.com", "manager@example.com"]
|
||||||
|
email_password_resets = true
|
||||||
|
|
||||||
|
[auth.openid_connect]
|
||||||
|
enabled = true
|
||||||
|
client_id = "mediamanager-client"
|
||||||
|
client_secret = "your-secret-key-here"
|
||||||
|
configuration_endpoint = "https://auth.example.com/.well-known/openid-configuration"
|
||||||
|
name = "Authentik"
|
||||||
|
```
|
||||||
|
|
||||||
42
docs/configuration/backend.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
description: >-
|
||||||
|
These settings configure the core backend application through the config.toml
|
||||||
|
file. All backend configuration is now centralized in this TOML file instead
|
||||||
|
of environment variables.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Backend
|
||||||
|
|
||||||
|
## General Settings (`[misc]`)
|
||||||
|
|
||||||
|
* `frontend_url`\
|
||||||
|
The URL the frontend will be accessed from. This is required. Do not include a trailing slash. Default is `http://localhost:8000`.
|
||||||
|
|
||||||
|
Example: if you are accessing MediaManager at `http://example.com/media`, set this to `http://example.com/media`.
|
||||||
|
|
||||||
|
If you are accessing MediaManager at the root of a domain, e.g. `https://mediamanager.example.com`, set this to `https://mediamanager.example.com`.
|
||||||
|
|
||||||
|
`frontend_url` does not affect where the server binds. It also does not configure a base path prefix. For prefixes, see [URL Prefix](../advanced-features/url-prefix.md).
|
||||||
|
* `cors_urls`\
|
||||||
|
A list of origins you are going to access the API from. Do not include trailing slashes.
|
||||||
|
* `development`\
|
||||||
|
Set to `true` to enable development mode. Default is `false`.
|
||||||
|
|
||||||
|
## Example Configuration
|
||||||
|
|
||||||
|
Here's a complete example of the general settings section in your `config.toml`:
|
||||||
|
|
||||||
|
```toml title="config.toml"
|
||||||
|
[misc]
|
||||||
|
|
||||||
|
# REQUIRED: Change this to match your actual frontend domain.
|
||||||
|
frontend_url = "http://mediamanager.dev"
|
||||||
|
|
||||||
|
cors_urls = ["http://localhost:8000"]
|
||||||
|
|
||||||
|
# Optional: Development mode (set to true for debugging)
|
||||||
|
development = false
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
The `frontend_url` is the most important setting to configure correctly. Make sure it matches your actual deployment URLs.
|
||||||
52
docs/configuration/custom-libraries.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Custom Libraries
|
||||||
|
|
||||||
|
MediaManager supports custom libraries, allowing you to add multiple folders for your movies and TV series. This feature is useful if you organize your media into different directories. For example, you might have separate folders for "Action" movies and "Comedy" movies, or "Live Action" TV shows and "Animated" TV shows.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Custom libraries are configured in the `misc` section in the `config.toml` file. You can add as many libraries as you need.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
You are not limited to `/data/tv` or `/data/movies`, you can choose the entire path freely!
|
||||||
|
|
||||||
|
### Movie Libraries
|
||||||
|
|
||||||
|
To add custom movie libraries, add a `[[misc.movie_libraries]]` section for each library. Each library requires a `name` and a `path`.
|
||||||
|
|
||||||
|
Example — configuring two movie libraries:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[misc]
|
||||||
|
|
||||||
|
# ... other misc settings
|
||||||
|
|
||||||
|
[[misc.movie_libraries]]
|
||||||
|
name = "Action"
|
||||||
|
path = "/data/movies/action"
|
||||||
|
|
||||||
|
[[misc.movie_libraries]]
|
||||||
|
name = "Comedy"
|
||||||
|
path = "/data/movies/comedy"
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, MediaManager will scan both `/data/movies/action` and `/data/movies/comedy` for movies.
|
||||||
|
|
||||||
|
### TV Show Libraries
|
||||||
|
|
||||||
|
Similarly, to add custom TV show libraries, add a `[[misc.tv_libraries]]` section for each library. Each library requires a `name` and a `path`.
|
||||||
|
|
||||||
|
Example — configuring two TV show libraries:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[misc]
|
||||||
|
|
||||||
|
# ... other misc settings
|
||||||
|
|
||||||
|
[[misc.tv_libraries]]
|
||||||
|
name = "Live Action"
|
||||||
|
path = "/data/tv/live-action"
|
||||||
|
|
||||||
|
[[misc.tv_libraries]]
|
||||||
|
name = "Animation"
|
||||||
|
path = "/data/tv/animation"
|
||||||
|
```
|
||||||
32
docs/configuration/database.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Database
|
||||||
|
|
||||||
|
Database settings are configured in the `[database]` section of your `config.toml` file. MediaManager uses PostgreSQL as its database backend.
|
||||||
|
|
||||||
|
## Database Settings (`[database]`)
|
||||||
|
|
||||||
|
* `host`\
|
||||||
|
Hostname or IP of the PostgreSQL server. Default is `localhost`.
|
||||||
|
* `port`\
|
||||||
|
Port number of the PostgreSQL server. Default is `5432`.
|
||||||
|
* `user`\
|
||||||
|
Username for the PostgreSQL connection. Default is `MediaManager`.
|
||||||
|
* `password`\
|
||||||
|
Password for the PostgreSQL user. Default is `MediaManager`.
|
||||||
|
* `dbname`\
|
||||||
|
Name of the PostgreSQL database. Default is `MediaManager`.
|
||||||
|
|
||||||
|
## Example Configuration
|
||||||
|
|
||||||
|
Here's a complete example of the database section in your `config.toml`:
|
||||||
|
|
||||||
|
```toml title="config.toml"
|
||||||
|
[database]
|
||||||
|
host = "db"
|
||||||
|
port = 5432
|
||||||
|
user = "MediaManager"
|
||||||
|
password = "your_secure_password"
|
||||||
|
dbname = "MediaManager"
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
In docker-compose deployments the container name is simultaneously its hostname, so you can use "db" or "postgres" as host.
|
||||||
@@ -6,89 +6,59 @@ Download client settings are configured in the `[torrents]` section of your `con
|
|||||||
|
|
||||||
qBittorrent is a popular BitTorrent client that MediaManager can integrate with for downloading torrents.
|
qBittorrent is a popular BitTorrent client that MediaManager can integrate with for downloading torrents.
|
||||||
|
|
||||||
- `enabled`
|
* `enabled`\
|
||||||
|
Set to `true` to enable qBittorrent integration. Default is `false`.
|
||||||
Set to `true` to enable qBittorrent integration. Default is `false`.
|
* `host`\
|
||||||
|
Hostname or IP of the qBittorrent server. Include the protocol (http/https).
|
||||||
- `host`
|
* `port`\
|
||||||
|
Port of the qBittorrent Web UI/API. Default is `8080`.
|
||||||
Hostname or IP of the qBittorrent server. Include the protocol (http/https).
|
* `username`\
|
||||||
|
Username for qBittorrent Web UI authentication. Default is `admin`.
|
||||||
- `port`
|
* `password`\
|
||||||
|
Password for qBittorrent Web UI authentication. Default is `admin`.
|
||||||
Port of the qBittorrent Web UI/API. Default is `8080`.
|
|
||||||
|
|
||||||
- `username`
|
|
||||||
|
|
||||||
Username for qBittorrent Web UI authentication. Default is `admin`.
|
|
||||||
|
|
||||||
- `password`
|
|
||||||
|
|
||||||
Password for qBittorrent Web UI authentication. Default is `admin`.
|
|
||||||
|
|
||||||
## Transmission Settings (`[torrents.transmission]`)
|
## Transmission Settings (`[torrents.transmission]`)
|
||||||
|
|
||||||
<note>
|
!!! info
|
||||||
|
The downloads path in Transmission and MediaManager must be the same, i.e. the path `/data/torrents` must link to the same volume for both containers.
|
||||||
The downloads path in Transmission and MediaManager __must__ be the same, i.e. the path `/data/torrents` must link to the same volume for both containers.
|
|
||||||
|
|
||||||
</note>
|
|
||||||
|
|
||||||
Transmission is a BitTorrent client that MediaManager can integrate with for downloading torrents.
|
Transmission is a BitTorrent client that MediaManager can integrate with for downloading torrents.
|
||||||
|
|
||||||
- `enabled`
|
* `enabled`\
|
||||||
|
Set to `true` to enable Transmission integration. Default is `false`.
|
||||||
Set to `true` to enable Transmission integration. Default is `false`.
|
* `username`\
|
||||||
|
Username for Transmission RPC authentication.
|
||||||
- `username`
|
* `password`\
|
||||||
|
Password for Transmission RPC authentication.
|
||||||
Username for Transmission RPC authentication.
|
* `https_enabled`\
|
||||||
|
Set to `true` if your Transmission RPC endpoint uses HTTPS. Default is `true`.
|
||||||
- `password`
|
* `host`\
|
||||||
|
Hostname or IP of the Transmission server (without protocol).
|
||||||
Password for Transmission RPC authentication.
|
* `port`\
|
||||||
|
Port of the Transmission RPC endpoint. Default is `9091`.
|
||||||
- `https_enabled`
|
* `path`\
|
||||||
|
RPC request path target. Usually `/transmission/rpc`.
|
||||||
Set to `true` if your Transmission RPC endpoint uses HTTPS. Default is `true`.
|
|
||||||
|
|
||||||
- `host`
|
|
||||||
|
|
||||||
Hostname or IP of the Transmission server (without protocol).
|
|
||||||
|
|
||||||
- `port`
|
|
||||||
|
|
||||||
Port of the Transmission RPC endpoint. Default is `9091`.
|
|
||||||
|
|
||||||
- `path`
|
|
||||||
|
|
||||||
RPC request path target. Usually `/transmission/rpc`.
|
|
||||||
|
|
||||||
## SABnzbd Settings (`[torrents.sabnzbd]`)
|
## SABnzbd Settings (`[torrents.sabnzbd]`)
|
||||||
|
|
||||||
SABnzbd is a Usenet newsreader that MediaManager can integrate with for downloading NZB files.
|
SABnzbd is a Usenet newsreader that MediaManager can integrate with for downloading NZB files.
|
||||||
|
|
||||||
- `enabled`
|
* `enabled`\
|
||||||
|
Set to `true` to enable SABnzbd integration. Default is `false`.
|
||||||
Set to `true` to enable SABnzbd integration. Default is `false`.
|
* `host`\
|
||||||
|
Hostname or IP of the SABnzbd server, it needs to include `http(s)://`.
|
||||||
- `host`
|
* `port`\
|
||||||
|
Port of the SABnzbd API. Default is `8080`.
|
||||||
Hostname or IP of the SABnzbd server.
|
* `api_key`\
|
||||||
|
API key for SABnzbd. You can find this in SABnzbd's configuration under "General" → "API Key".
|
||||||
- `port`
|
* `base_path`\
|
||||||
|
API base path for SABnzbd. It usually ends with `/api`, the default is `/api`.
|
||||||
Port of the SABnzbd API. Default is `8080`.
|
|
||||||
|
|
||||||
- `api_key`
|
|
||||||
|
|
||||||
API key for SABnzbd. You can find this in SABnzbd's configuration under "General" → "API Key".
|
|
||||||
|
|
||||||
## Example Configuration
|
## Example Configuration
|
||||||
|
|
||||||
Here's a complete example of the download clients section in your `config.toml`:
|
Here's a complete example of the download clients section in your `config.toml`:
|
||||||
|
|
||||||
```toml
|
```toml title="config.toml"
|
||||||
[torrents]
|
[torrents]
|
||||||
# qBittorrent configuration
|
# qBittorrent configuration
|
||||||
[torrents.qbittorrent]
|
[torrents.qbittorrent]
|
||||||
@@ -111,7 +81,7 @@ Here's a complete example of the download clients section in your `config.toml`:
|
|||||||
# SABnzbd configuration
|
# SABnzbd configuration
|
||||||
[torrents.sabnzbd]
|
[torrents.sabnzbd]
|
||||||
enabled = false
|
enabled = false
|
||||||
host = "sabnzbd"
|
host = "http://sabnzbd"
|
||||||
port = 8080
|
port = 8080
|
||||||
api_key = "your_sabnzbd_api_key"
|
api_key = "your_sabnzbd_api_key"
|
||||||
```
|
```
|
||||||
@@ -120,7 +90,7 @@ Here's a complete example of the download clients section in your `config.toml`:
|
|||||||
|
|
||||||
When using Docker Compose, make sure your download clients are accessible from the MediaManager backend:
|
When using Docker Compose, make sure your download clients are accessible from the MediaManager backend:
|
||||||
|
|
||||||
```yaml
|
```yaml title="docker-compose.yml"
|
||||||
services:
|
services:
|
||||||
# MediaManager backend
|
# MediaManager backend
|
||||||
backend:
|
backend:
|
||||||
@@ -148,10 +118,8 @@ services:
|
|||||||
# ... other configuration ...
|
# ... other configuration ...
|
||||||
```
|
```
|
||||||
|
|
||||||
<note>
|
!!! warning
|
||||||
You should enable only one BitTorrent and only one Usenet Download Client at any time.
|
You should enable only one BitTorrent and only one Usenet Download Client at any time.
|
||||||
</note>
|
|
||||||
|
|
||||||
<tip>
|
!!! info
|
||||||
Make sure the download directories in your download clients are accessible to MediaManager for proper file management and organization.
|
Make sure the download directories in your download clients are accessible to MediaManager for proper file management and organization.
|
||||||
</tip>
|
|
||||||
67
docs/configuration/indexers.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# Indexers
|
||||||
|
|
||||||
|
Indexer settings are configured in the `[indexers]` section of your `config.toml` file. MediaManager supports both Prowlarr and Jackett as indexer providers.
|
||||||
|
|
||||||
|
## Prowlarr (`[indexers.prowlarr]`)
|
||||||
|
|
||||||
|
* `enabled`\
|
||||||
|
Set to `true` to enable Prowlarr. Default is `false`.
|
||||||
|
* `url`\
|
||||||
|
Base URL of your Prowlarr instance.
|
||||||
|
* `api_key`\
|
||||||
|
API key for Prowlarr. You can find this in Prowlarr's settings under General.
|
||||||
|
* `timeout_seconds`\
|
||||||
|
Timeout in seconds for requests to Prowlarr. Default is `60`.
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
Symptoms of timeouts are typically no search results ("No torrents found!") in conjunction with logs showing read timeouts.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary>Example timeout log</summary>
|
||||||
|
|
||||||
|
```none
|
||||||
|
DEBUG - media_manager.indexer.utils -
|
||||||
|
follow_redirects_to_final_torrent_url():
|
||||||
|
An error occurred during the request for <some-url>:
|
||||||
|
HTTPConnectionPool(host='<some-host>', port=<some-port>):
|
||||||
|
Read timed out. (read timeout=10)
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Jackett (`[indexers.jackett]`)
|
||||||
|
|
||||||
|
* `enabled`\
|
||||||
|
Set to `true` to enable Jackett. Default is `false`.
|
||||||
|
* `url`\
|
||||||
|
Base URL of your Jackett instance.
|
||||||
|
* `api_key`\
|
||||||
|
API key for Jackett. You can find this in Jackett's dashboard.
|
||||||
|
* `indexers`\
|
||||||
|
List of indexer names to use with Jackett.
|
||||||
|
* `timeout_seconds`\
|
||||||
|
Timeout in seconds for requests to Jackett. Refer to the Prowlarr section for details.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Example Configuration
|
||||||
|
|
||||||
|
```toml title="config.toml"
|
||||||
|
[indexers]
|
||||||
|
[indexers.prowlarr]
|
||||||
|
enabled = true
|
||||||
|
url = "http://prowlarr:9696"
|
||||||
|
api_key = "your_prowlarr_api_key"
|
||||||
|
timeout_seconds = 60
|
||||||
|
|
||||||
|
[indexers.jackett]
|
||||||
|
enabled = false
|
||||||
|
url = "http://jackett:9117"
|
||||||
|
api_key = "your_jackett_api_key"
|
||||||
|
indexers = ["1337x", "rarbg"]
|
||||||
|
timeout_seconds = 60
|
||||||
|
```
|
||||||
|
|
||||||
12
docs/configuration/logging.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Logging
|
||||||
|
|
||||||
|
MediaManager automatically logs events and errors to help with troubleshooting and monitoring. These logs are emitted to the console (stdout) by default, and to a json-formatted log file.
|
||||||
|
|
||||||
|
## Configuring Logging
|
||||||
|
|
||||||
|
The following are configured as environment variables.
|
||||||
|
|
||||||
|
* `LOG_FILE`\
|
||||||
|
Path to the JSON log file. Default is `/app/config/media_manager.log`. The directory must exist and be writable.
|
||||||
|
* `MEDIAMANAGER_LOG_LEVEL`\
|
||||||
|
Logging level. Default is `INFO`. Supported values: `DEBUG`, `INFO`, `WARNING`, `ERROR`.
|
||||||
@@ -2,87 +2,62 @@
|
|||||||
|
|
||||||
These settings are configured in the `[notifications]` section of your `config.toml` file.
|
These settings are configured in the `[notifications]` section of your `config.toml` file.
|
||||||
|
|
||||||
### SMTP Configuration (`[notifications.smtp_config]`)
|
## SMTP Configuration (`[notifications.smtp_config]`)
|
||||||
|
|
||||||
For sending emails, MediaManager uses the SMTP protocol. You can use any SMTP server, like Gmail or SMTP2GO.
|
For sending emails, MediaManager uses the SMTP protocol. You can use any SMTP server, like Gmail or SMTP2GO.
|
||||||
|
|
||||||
- `smtp_host`
|
* `smtp_host`\
|
||||||
|
Hostname of the SMTP server.
|
||||||
|
* `smtp_port`\
|
||||||
|
Port of the SMTP server.
|
||||||
|
* `smtp_user`\
|
||||||
|
Username for the SMTP server.
|
||||||
|
* `smtp_password`\
|
||||||
|
Password (or app password) for the SMTP server.
|
||||||
|
* `from_email`\
|
||||||
|
From-address used when sending emails.
|
||||||
|
* `use_tls`\
|
||||||
|
Set to `true` to use TLS for the SMTP connection. Default is `true`.
|
||||||
|
|
||||||
Hostname of the SMTP server.
|
## Email Notifications (`[notifications.email_notifications]`)
|
||||||
|
|
||||||
- `smtp_port`
|
Controls which emails receive notifications.
|
||||||
|
|
||||||
Port of the SMTP server.
|
* `enabled`\
|
||||||
|
Set to `true` to enable email notifications. Default is `false`.
|
||||||
|
* `emails`\
|
||||||
|
List of email addresses to send notifications to.
|
||||||
|
|
||||||
- `smtp_user`
|
## Gotify Notifications (`[notifications.gotify]`)
|
||||||
|
|
||||||
Username for the SMTP server.
|
* `enabled`\
|
||||||
|
Set to `true` to enable Gotify notifications. Default is `false`.
|
||||||
|
* `api_key`\
|
||||||
|
API key for Gotify.
|
||||||
|
* `url`\
|
||||||
|
Base URL of your Gotify instance. Do not include a trailing slash.
|
||||||
|
|
||||||
- `smtp_password`
|
## Ntfy Notifications (`[notifications.ntfy]`)
|
||||||
|
|
||||||
Password or app password for the SMTP server.
|
* `enabled`\
|
||||||
|
Set to `true` to enable Ntfy notifications. Default is `false`.
|
||||||
|
* `url`\
|
||||||
|
URL of your ntfy instance plus the topic.
|
||||||
|
|
||||||
- `from_email`
|
## Pushover Notifications (`[notifications.pushover]`)
|
||||||
|
|
||||||
Email address from which emails will be sent.
|
* `enabled`\
|
||||||
|
Set to `true` to enable Pushover notifications. Default is `false`.
|
||||||
- `use_tls`
|
* `api_key`\
|
||||||
|
API key for Pushover.
|
||||||
Set to `true` to use TLS for the SMTP connection. Default is `true`.
|
* `user`\
|
||||||
|
User key for Pushover.
|
||||||
### Email Notifications (`[notifications.email_notifications]`)
|
|
||||||
|
|
||||||
- `enabled`
|
|
||||||
|
|
||||||
Set to `true` to enable email notifications. Default is `false`.
|
|
||||||
|
|
||||||
- `emails`
|
|
||||||
|
|
||||||
List of email addresses to send notifications to.
|
|
||||||
|
|
||||||
### Gotify Notifications (`[notifications.gotify]`)
|
|
||||||
|
|
||||||
- `enabled`
|
|
||||||
|
|
||||||
Set to `true` to enable Gotify notifications. Default is `false`.
|
|
||||||
|
|
||||||
- `api_key`
|
|
||||||
|
|
||||||
API key for Gotify.
|
|
||||||
|
|
||||||
- `url`
|
|
||||||
|
|
||||||
Base URL of your Gotify instance. Note the lack of a trailing slash.
|
|
||||||
|
|
||||||
### Ntfy Notifications (`[notifications.ntfy]`)
|
|
||||||
|
|
||||||
- `enabled`
|
|
||||||
|
|
||||||
Set to `true` to enable Ntfy notifications. Default is `false`.
|
|
||||||
|
|
||||||
- `url`
|
|
||||||
|
|
||||||
URL of your ntfy instance plus the topic.
|
|
||||||
|
|
||||||
### Pushover Notifications (`[notifications.pushover]`)
|
|
||||||
|
|
||||||
- `enabled`
|
|
||||||
|
|
||||||
Set to `true` to enable Pushover notifications. Default is `false`.
|
|
||||||
|
|
||||||
- `api_key`
|
|
||||||
|
|
||||||
API key for Pushover.
|
|
||||||
|
|
||||||
- `user`
|
|
||||||
|
|
||||||
User key for Pushover.
|
|
||||||
|
|
||||||
## Example Configuration
|
## Example Configuration
|
||||||
|
|
||||||
Here's a complete example of the notifications section in your `config.toml`:
|
Here's a complete example of the notifications section in your `config.toml`:
|
||||||
|
|
||||||
```toml
|
```toml title="config.toml"
|
||||||
[notifications]
|
[notifications]
|
||||||
# SMTP settings for email notifications and password resets
|
# SMTP settings for email notifications and password resets
|
||||||
[notifications.smtp_config]
|
[notifications.smtp_config]
|
||||||
@@ -116,6 +91,6 @@ Here's a complete example of the notifications section in your `config.toml`:
|
|||||||
user = "your_pushover_user_key"
|
user = "your_pushover_user_key"
|
||||||
```
|
```
|
||||||
|
|
||||||
<note>
|
|
||||||
|
!!! info
|
||||||
You can enable multiple notification methods simultaneously. For example, you could have both email and Gotify notifications enabled at the same time.
|
You can enable multiple notification methods simultaneously. For example, you could have both email and Gotify notifications enabled at the same time.
|
||||||
</note>
|
|
||||||
133
docs/configuration/scoring-rulesets.md
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
# Scoring Rulesets
|
||||||
|
|
||||||
|
Scoring rulesets in MediaManager allow you to flexibly control which releases are preferred or avoided when searching for media. Each ruleset is a collection of scoring rules that can be assigned to one or more libraries. When MediaManager evaluates releases, it applies the relevant ruleset(s) to adjust the score of each result, influencing which releases are selected for download.
|
||||||
|
|
||||||
|
## How Rulesets Work
|
||||||
|
|
||||||
|
* Rulesets are defined in the configuration and contain a list of rule names and the libraries they apply to.
|
||||||
|
* Scoring rules can target keywords in release titles or specific indexer flags.
|
||||||
|
* When searching for a release, MediaManager checks which library the media belongs to and applies the corresponding ruleset.
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
Rules define how MediaManager scores releases based on their titles or indexer flags. You can create rules that:
|
||||||
|
|
||||||
|
* Prefer releases with specific codecs (e.g., H.265 over H.264).
|
||||||
|
* Avoid releases with certain keywords (e.g., "CAM", "TS", "Nuked").
|
||||||
|
* Reject releases that do not meet certain criteria (e.g., non-freeleech releases).
|
||||||
|
* and more.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
The keywords and flags are compared case-insensitively.
|
||||||
|
|
||||||
|
### Title Rules
|
||||||
|
|
||||||
|
Title rules allow you to adjust the score of a release based on the presence (or absence) of specific keywords in the release title. This is useful for preferring or avoiding certain encodings, sources, or other characteristics that are typically included in release names.
|
||||||
|
|
||||||
|
Each title rule consists of:
|
||||||
|
|
||||||
|
* `name`\
|
||||||
|
A unique identifier for the rule.
|
||||||
|
* `keywords`\
|
||||||
|
List of keywords to search for in the release title.
|
||||||
|
* `score_modifier`\
|
||||||
|
Amount to add or subtract from the score if a keyword matches.
|
||||||
|
* `negate`\
|
||||||
|
If `true`, the rule applies when none of the keywords are present.
|
||||||
|
|
||||||
|
Examples for Title Rules
|
||||||
|
|
||||||
|
```toml title="config.toml"
|
||||||
|
[[indexers.title_scoring_rules]]
|
||||||
|
name = "prefer_h265"
|
||||||
|
keywords = ["h265", "hevc", "x265"]
|
||||||
|
score_modifier = 100
|
||||||
|
negate = false
|
||||||
|
|
||||||
|
[[indexers.title_scoring_rules]]
|
||||||
|
name = "avoid_cam"
|
||||||
|
keywords = ["cam", "ts"]
|
||||||
|
score_modifier = -10000
|
||||||
|
negate = false
|
||||||
|
```
|
||||||
|
|
||||||
|
* The first rule increases the score for releases containing "h265", "hevc", or "x265".
|
||||||
|
* The second rule heavily penalizes releases containing "cam" or "ts".
|
||||||
|
|
||||||
|
If `negate` is set to `true`, the `score_modifier` is applied only if none of the keywords are found in the title.
|
||||||
|
|
||||||
|
### Indexer Flag Rules
|
||||||
|
|
||||||
|
Indexer flag rules adjust the score based on flags provided by the indexer (such as `freeleech`, `nuked`, etc). These flags are often used to indicate special properties or warnings about a release.
|
||||||
|
|
||||||
|
Each indexer flag rule consists of:
|
||||||
|
|
||||||
|
* `name`\
|
||||||
|
A unique identifier for the rule.
|
||||||
|
* `flags`\
|
||||||
|
List of indexer flags to match.
|
||||||
|
* `score_modifier`\
|
||||||
|
Amount to add or subtract from the score if a flag matches.
|
||||||
|
* `negate`\
|
||||||
|
If `true`, the rule applies when none of the flags are present.
|
||||||
|
|
||||||
|
Examples for Indexer Flag Rules
|
||||||
|
|
||||||
|
```toml title="config.toml"
|
||||||
|
[[indexers.indexer_flag_scoring_rules]]
|
||||||
|
name = "reject_non_freeleech"
|
||||||
|
flags = ["freeleech", "freeleech75"]
|
||||||
|
score_modifier = -10000
|
||||||
|
negate = true
|
||||||
|
|
||||||
|
[[indexers.indexer_flag_scoring_rules]]
|
||||||
|
name = "reject_nuked"
|
||||||
|
flags = ["nuked"]
|
||||||
|
score_modifier = -10000
|
||||||
|
negate = false
|
||||||
|
```
|
||||||
|
|
||||||
|
* The first rule penalizes releases that do not have the "freeleech" or "freeleech75" flag.
|
||||||
|
* The second rule penalizes releases that are marked as "nuked".
|
||||||
|
|
||||||
|
If `negate` is set to `true`, the `score_modifier` is applied only if none of the flags are present on the release.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```toml title="config.toml"
|
||||||
|
[[indexers.scoring_rule_sets]]
|
||||||
|
name = "default"
|
||||||
|
libraries = ["ALL_TV", "ALL_MOVIES"]
|
||||||
|
rule_names = ["prefer_h265", "avoid_cam", "reject_nuked"]
|
||||||
|
|
||||||
|
[[indexers.scoring_rule_sets]]
|
||||||
|
name = "strict_quality"
|
||||||
|
libraries = ["ALL_MOVIES"]
|
||||||
|
rule_names = ["prefer_h265", "avoid_cam", "reject_non_freeleech"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Libraries
|
||||||
|
|
||||||
|
The libraries that are mentioned in the preceding example are explained in greater detail in the [Library config section](custom-libraries.md).
|
||||||
|
|
||||||
|
### Special Libraries
|
||||||
|
|
||||||
|
You can use special library names in your rulesets:
|
||||||
|
|
||||||
|
* `ALL_TV`: Applies the ruleset to all TV libraries.
|
||||||
|
* `ALL_MOVIES`: Applies the ruleset to all movie libraries.
|
||||||
|
* `Default`: Applies the ruleset to all media that is not part of a custom library.
|
||||||
|
|
||||||
|
This allows you to set global rules for all TV or movie content, or provide fallback rules for uncategorized media.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
You don't need to create lots of libraries with different directories, multiple libraries can share the same directory. You can set multiple (unlimited) libraries to the default directory `/data/movies` or `/data/tv` and use different rulesets with them.
|
||||||
|
|
||||||
|
## Relation to Sonarr/Radarr Profiles
|
||||||
|
|
||||||
|
MediaManager's scoring rules and rulesets system is an alternative to Sonarr's Quality, Custom, and Release Profiles. This system is designed to be more intuitive and flexible.
|
||||||
|
|
||||||
|
* Quality Profiles: Use scoring rules to prefer or avoid certain codecs, resolutions, or other quality indicators.
|
||||||
|
* Custom/Release Profiles: Use title or flag-based rules to match or exclude releases based on keywords or indexer flags.
|
||||||
|
|
||||||
|
This approach provides a powerful and transparent way to fine-tune your automation.
|
||||||