Compare commits
2182 commits
v1.29.4_44
...
main
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e1739ac4fc | ||
![]() |
8736c77f7a | ||
![]() |
338a028185 | ||
![]() |
e2d0e944eb | ||
![]() |
f53b70571b | ||
![]() |
2814de4420 | ||
![]() |
024fe1141b | ||
![]() |
086a957a2b | ||
![]() |
84c5b08c25 | ||
![]() |
7e8488694d | ||
![]() |
231b89c9c0 | ||
![]() |
d5f6584e1d | ||
![]() |
7702560b12 | ||
![]() |
982183600d | ||
![]() |
933c24ea6f | ||
![]() |
05e9697dff | ||
![]() |
259700c45f | ||
![]() |
22d79850f6 | ||
![]() |
56aed8246d | ||
![]() |
ca1be71bca | ||
![]() |
6111bf157e | ||
![]() |
2195730fa6 | ||
![]() |
1dc832d392 | ||
![]() |
1a63d3837e | ||
![]() |
bdbaa166d9 | ||
![]() |
812e67d55d | ||
![]() |
dfd6846deb | ||
![]() |
6d3421a505 | ||
![]() |
ede9de146a | ||
![]() |
6959cf689b | ||
![]() |
36ba48b8ae | ||
![]() |
8a2b36ad55 | ||
![]() |
6000c7f3bc | ||
![]() |
5aa658de59 | ||
![]() |
6673f1eb24 | ||
![]() |
a02e91169d | ||
![]() |
ec92608024 | ||
![]() |
5a50d32748 | ||
![]() |
cbdcbd3ab4 | ||
![]() |
cb00d45e3d | ||
![]() |
2b2b1bba63 | ||
![]() |
031420bc78 | ||
![]() |
387faa13d5 | ||
![]() |
6979d43650 | ||
![]() |
af8bb132d0 | ||
![]() |
40964187bb | ||
![]() |
fe3d951f26 | ||
![]() |
6e365b37db | ||
![]() |
5e55a17b2a | ||
![]() |
ffecfbe075 | ||
![]() |
644e52b153 | ||
![]() |
b396e0eee3 | ||
![]() |
8b6a79ad9e | ||
![]() |
696900228b | ||
![]() |
e5d083fe79 | ||
![]() |
d4b3fb942f | ||
![]() |
527d602a9f | ||
![]() |
513f252a0c | ||
![]() |
0fe704c6f9 | ||
![]() |
5a2fc20b20 | ||
![]() |
2a45ad147c | ||
![]() |
fa3f2237eb | ||
![]() |
6aa356e69f | ||
![]() |
a04360f625 | ||
![]() |
48c9e66ae5 | ||
![]() |
05ca555b6e | ||
![]() |
2bb75b6aa9 | ||
![]() |
869d400617 | ||
![]() |
6ae7a92e03 | ||
![]() |
a67f57c0e0 | ||
![]() |
b04cd4edee | ||
![]() |
cd1b6e6976 | ||
![]() |
1fa5e220a1 | ||
![]() |
b21b7f0721 | ||
![]() |
21ed8d5c79 | ||
![]() |
6ac4e98d4b | ||
![]() |
b0db8ed6c4 | ||
![]() |
6522707b49 | ||
![]() |
9483c456d4 | ||
![]() |
5781ae9d82 | ||
![]() |
4d727708e2 | ||
![]() |
5c1c174db1 | ||
![]() |
cffdd9c86a | ||
![]() |
ebd64ded62 | ||
![]() |
0758d55dea | ||
![]() |
3992119e32 | ||
![]() |
87871e4df9 | ||
![]() |
ef45e9f490 | ||
![]() |
4e5eef129d | ||
![]() |
034b308ddc | ||
![]() |
3aa2927dae | ||
![]() |
c04340c63e | ||
![]() |
f97dca7707 | ||
![]() |
cf58649a99 | ||
![]() |
e65d1d5930 | ||
![]() |
ad06502539 | ||
![]() |
1ffe862810 | ||
![]() |
69d096df17 | ||
![]() |
6d1b325b34 | ||
![]() |
698226634e | ||
![]() |
0108211c0f | ||
![]() |
155ccbc870 | ||
![]() |
f222e47651 | ||
![]() |
4684094b9b | ||
![]() |
8a8d3811b9 | ||
![]() |
309be88ccd | ||
![]() |
4987bbb712 | ||
![]() |
a6676907b4 | ||
![]() |
df9ec9327d | ||
![]() |
030cd8c4c4 | ||
![]() |
6e10d15f2c | ||
![]() |
6eadca330b | ||
![]() |
309ba7d67e | ||
![]() |
106bae4a31 | ||
![]() |
95280fd692 | ||
![]() |
82fffd2c56 | ||
![]() |
ff275ea175 | ||
![]() |
8c2851fbc4 | ||
![]() |
c1d9ce8679 | ||
![]() |
29c154c681 | ||
![]() |
a861b93d7d | ||
![]() |
0280d15d9d | ||
![]() |
c8aa782fef | ||
![]() |
8ff4a08a2c | ||
![]() |
af1113bf9e | ||
![]() |
55f7cf3ca9 | ||
![]() |
c72063280c | ||
![]() |
b06c2b786c | ||
![]() |
c607615e41 | ||
![]() |
566471444f | ||
![]() |
bf82ce24e0 | ||
![]() |
afd78652f2 | ||
![]() |
0f657da5a4 | ||
![]() |
55fa3234fd | ||
![]() |
f094ff2aa1 | ||
![]() |
a13052e24c | ||
![]() |
bc2c73e499 | ||
![]() |
bcb885422a | ||
![]() |
c438e17954 | ||
![]() |
c46e82561e | ||
![]() |
5c0821330f | ||
![]() |
69e0db56b3 | ||
![]() |
e8bf498236 | ||
![]() |
9cf40afaf0 | ||
![]() |
28a15365d6 | ||
![]() |
d49b353c49 | ||
![]() |
8b966a0f15 | ||
![]() |
30e9763888 | ||
![]() |
0f596e278c | ||
![]() |
74ad8b37bb | ||
![]() |
ec6b56f63c | ||
![]() |
1fbbb5a236 | ||
![]() |
725f30c494 | ||
![]() |
347e6191c5 | ||
![]() |
94c8fe1098 | ||
![]() |
cb32b5cd7b | ||
![]() |
ddf04a7eb4 | ||
![]() |
acf099e481 | ||
![]() |
f7ada7351e | ||
![]() |
2a5cf20c9f | ||
![]() |
81259115d1 | ||
![]() |
f20a6cb321 | ||
![]() |
81af3b6f20 | ||
![]() |
235b82b3fc | ||
![]() |
e2317ea35e | ||
![]() |
f5d73b0499 | ||
![]() |
c1239a7337 | ||
![]() |
7e38e7c949 | ||
![]() |
88b5f5b500 | ||
![]() |
983473261b | ||
![]() |
bfab86b70d | ||
![]() |
5b25d5140c | ||
![]() |
904756bbc5 | ||
![]() |
3a6e2c92cf | ||
![]() |
4ade8eae17 | ||
![]() |
41d43acf5f | ||
![]() |
fa71641ea4 | ||
![]() |
fce8d48de6 | ||
![]() |
6d310d6297 | ||
![]() |
f5ce3deb3a | ||
![]() |
767fe87b2e | ||
![]() |
f2877c3a6e | ||
![]() |
adae5dd758 | ||
![]() |
5118d261ab | ||
![]() |
cc15c5c69f | ||
![]() |
ec51a9f6d6 | ||
![]() |
9b2ac6aaca | ||
![]() |
4daf2478aa | ||
![]() |
63a745c7ad | ||
![]() |
47a4984a56 | ||
![]() |
82f12b8ee6 | ||
![]() |
c7b3039a1a | ||
![]() |
ed68c49c16 | ||
![]() |
a6af4892e3 | ||
![]() |
98f87c6548 | ||
![]() |
54d770df8a | ||
![]() |
b82db1edaa | ||
![]() |
87f02cc775 | ||
![]() |
69030ea9a7 | ||
![]() |
956ca816bc | ||
![]() |
343afea713 | ||
![]() |
f54e6fc09f | ||
![]() |
f4ef259ba0 | ||
![]() |
bced117eb4 | ||
![]() |
54b9bfaeef | ||
![]() |
c4f7cfc2a6 | ||
![]() |
4b722517f0 | ||
![]() |
6127fd4c5c | ||
![]() |
6214d510d6 | ||
![]() |
ecbe7beb6c | ||
![]() |
753dab8b3c | ||
![]() |
7a8f8e5472 | ||
![]() |
5d8af5f94c | ||
![]() |
5145c33ed4 | ||
![]() |
8f3ed8ba8e | ||
![]() |
dc4e6c4629 | ||
![]() |
a7cacafe25 | ||
![]() |
d25a245049 | ||
![]() |
14c7187539 | ||
![]() |
24670178dc | ||
![]() |
72fb421f54 | ||
![]() |
ac7e8bcdf4 | ||
![]() |
38983838fd | ||
![]() |
291159e7fc | ||
![]() |
464cf903f4 | ||
![]() |
935f471ccb | ||
![]() |
9fa9ad05b1 | ||
![]() |
0c482960ce | ||
![]() |
c3f8dc8c22 | ||
![]() |
a959f2a51d | ||
![]() |
880f4f61d2 | ||
![]() |
ab1d1ef4e7 | ||
![]() |
89255d0889 | ||
![]() |
14acee9090 | ||
![]() |
8399130f05 | ||
![]() |
1188012279 | ||
![]() |
04a8bde7ac | ||
![]() |
069a32dcdb | ||
![]() |
388144823a | ||
![]() |
c23d84be39 | ||
![]() |
66120025b7 | ||
![]() |
da33653b0a | ||
![]() |
3ea0210c1d | ||
![]() |
98f1e85c87 | ||
![]() |
d2509c619e | ||
![]() |
2bfe5d1573 | ||
![]() |
d7d464570f | ||
![]() |
2e82476cff | ||
![]() |
2f462717aa | ||
![]() |
86e04832a1 | ||
![]() |
96f1a271ef | ||
![]() |
55e3605ca4 | ||
![]() |
0bf55d8e32 | ||
![]() |
2dcad93d9c | ||
![]() |
328a58ac0d | ||
![]() |
7fca0d8da5 | ||
![]() |
413ab2c538 | ||
![]() |
394e0dfe37 | ||
![]() |
a9b6acec28 | ||
![]() |
ad4cbf20de | ||
![]() |
26fd797ac9 | ||
![]() |
35767591d2 | ||
![]() |
3b11854702 | ||
![]() |
895129c997 | ||
![]() |
92ec1ce77f | ||
![]() |
986bbfa831 | ||
![]() |
75c065c83a | ||
![]() |
9c0805c37a | ||
![]() |
bffc2cdf60 | ||
![]() |
a147dee4b6 | ||
![]() |
5423f1c25b | ||
![]() |
5c602bf4d4 | ||
![]() |
5db73c5c5c | ||
![]() |
52fe392a9e | ||
![]() |
5e1c0fb465 | ||
![]() |
37ab37bffc | ||
![]() |
664b7106ca | ||
![]() |
bb28cae671 | ||
![]() |
c2c26c471a | ||
![]() |
2dca2850dc | ||
![]() |
7fc8f6433b | ||
![]() |
f6180fccdc | ||
![]() |
9d01885b58 | ||
![]() |
ace0a5911c | ||
![]() |
21f2d3058a | ||
![]() |
26fd9d7e5f | ||
![]() |
c74ea7282a | ||
![]() |
279481ad54 | ||
![]() |
9e7a32804b | ||
![]() |
a0743d8b7d | ||
![]() |
68000c21a8 | ||
![]() |
e671b30aaf | ||
![]() |
cf1dfdc776 | ||
![]() |
de29480dda | ||
![]() |
2e424fe249 | ||
![]() |
d4ef6f52bb | ||
![]() |
e1e45f3f32 | ||
![]() |
330f4cadda | ||
![]() |
621eef0edc | ||
![]() |
33ce2b7bba | ||
![]() |
81792a5342 | ||
![]() |
5f43971ccf | ||
![]() |
38443a6068 | ||
![]() |
92bb42950e | ||
![]() |
b58edae134 | ||
![]() |
2b9f20a1b5 | ||
![]() |
d5f8199655 | ||
![]() |
d8903de92e | ||
![]() |
1d35965d03 | ||
![]() |
309bf1ad22 | ||
![]() |
0130591a0f | ||
![]() |
cf4ec06750 | ||
![]() |
e8712e6694 | ||
![]() |
ce5966c23d | ||
![]() |
68f6446718 | ||
![]() |
197f336b5f | ||
![]() |
cd375a976e | ||
![]() |
088d5addf2 | ||
![]() |
2377df9dae | ||
![]() |
ad5ba82f50 | ||
![]() |
b6f18cbe81 | ||
![]() |
87a0ba3db3 | ||
![]() |
3212a47720 | ||
![]() |
431536cdbb | ||
![]() |
9a60578088 | ||
![]() |
8dcd159bd6 | ||
![]() |
2f87463170 | ||
![]() |
9f56bf0ab9 | ||
![]() |
603b056512 | ||
![]() |
ce04e9e07a | ||
![]() |
c54a188154 | ||
![]() |
c77ba46d60 | ||
![]() |
cc3149c520 | ||
![]() |
512f672e9e | ||
![]() |
b117985f66 | ||
![]() |
b92a2b2a56 | ||
![]() |
a6f39bc74f | ||
![]() |
daad02504f | ||
![]() |
8a6889529c | ||
![]() |
b34cbd881a | ||
![]() |
f6eaaab725 | ||
![]() |
2a2c74e081 | ||
![]() |
c653e0f261 | ||
![]() |
f0dd1d715a | ||
![]() |
d98a2a5f79 | ||
![]() |
275717b8e3 | ||
![]() |
51dc197b33 | ||
![]() |
a42c95a781 | ||
![]() |
8b5b6d0821 | ||
![]() |
72dcde9e0f | ||
![]() |
a08a687951 | ||
![]() |
7ff68223ab | ||
![]() |
c76c1d6bf8 | ||
![]() |
0167407370 | ||
![]() |
b49b10141e | ||
![]() |
cb0e37e76e | ||
![]() |
237d1c1bf4 | ||
![]() |
cf71a41bae | ||
![]() |
52e09b4857 | ||
![]() |
aefd052888 | ||
![]() |
e47a11b8ba | ||
![]() |
2ad389f64e | ||
![]() |
d5e19e45cd | ||
![]() |
4a5654a247 | ||
![]() |
d4c60eab0d | ||
![]() |
0fb1d33f17 | ||
![]() |
3021eca8e5 | ||
![]() |
5921ec9a58 | ||
![]() |
3e3598fd92 | ||
![]() |
1aae29a0b8 | ||
![]() |
99c6f8fb13 | ||
![]() |
d4c23c8df8 | ||
![]() |
62a11283af | ||
![]() |
28d35bf04e | ||
![]() |
dd52ff2d33 | ||
![]() |
093347c7ab | ||
![]() |
755649a3c8 | ||
![]() |
6b25435b4f | ||
![]() |
2288b022bc | ||
![]() |
64e4ae7e4b | ||
![]() |
c6b4bc883b | ||
![]() |
50bc92aac0 | ||
![]() |
36b3521be8 | ||
![]() |
b05132a01a | ||
![]() |
9b418642a6 | ||
![]() |
013da0aa3d | ||
![]() |
8dcc01b2be | ||
![]() |
cf08ac7538 | ||
![]() |
5ead4af2dc | ||
![]() |
f2c20f60f7 | ||
![]() |
e0fc6b753c | ||
![]() |
ab3f82cfe4 | ||
![]() |
383f11019a | ||
![]() |
250f7fc55c | ||
![]() |
22172a680b | ||
![]() |
5156d76194 | ||
![]() |
decfb9687b | ||
![]() |
5e17b3199f | ||
![]() |
cfec6a8fdb | ||
![]() |
2ec63f7914 | ||
![]() |
29182cfc9a | ||
![]() |
5a7ef02387 | ||
![]() |
4b59f83288 | ||
![]() |
31987bc043 | ||
![]() |
23f0eb6fe8 | ||
![]() |
0994575bf3 | ||
![]() |
f4a12acd29 | ||
![]() |
335216f6dd | ||
![]() |
5a9acbc05b | ||
![]() |
219f99e516 | ||
![]() |
1890c0ab6b | ||
![]() |
a78e08bac1 | ||
![]() |
634169235a | ||
![]() |
45ffa65173 | ||
![]() |
62cb14e4b6 | ||
![]() |
3d7e9b7184 | ||
![]() |
d2807b8d6a | ||
![]() |
ed386dd12a | ||
![]() |
dadcf49eca | ||
![]() |
4a9f58bf9b | ||
![]() |
9d225d3d06 | ||
![]() |
268a9c4803 | ||
![]() |
bddeb03fd5 | ||
![]() |
f0bb50b61a | ||
![]() |
7e9fc4aa97 | ||
![]() |
e57c926676 | ||
![]() |
f3b17d8f73 | ||
![]() |
41af76bbe2 | ||
![]() |
9af5e7838f | ||
![]() |
5dacea6f74 | ||
![]() |
18fcca2884 | ||
![]() |
8222327299 | ||
![]() |
eebe9bcd5f | ||
![]() |
41befc0948 | ||
![]() |
09bf1c9175 | ||
![]() |
332a8d80f2 | ||
![]() |
56eb7bf0fc | ||
![]() |
99e9c2ada6 | ||
![]() |
d8ecefaea5 | ||
![]() |
b8d6cc1e09 | ||
![]() |
f36c40bc6b | ||
![]() |
83b63ca12e | ||
![]() |
f57acc0802 | ||
![]() |
29981b1088 | ||
![]() |
43f4dac3ad | ||
![]() |
2370c9ef41 | ||
![]() |
ebb50476ac | ||
![]() |
2ea080cacd | ||
![]() |
b56f22aac3 | ||
![]() |
9033e7f179 | ||
![]() |
9070a361bc | ||
![]() |
d8e66acd02 | ||
![]() |
687d896c63 | ||
![]() |
0243570c0b | ||
![]() |
66ccf298ba | ||
![]() |
982dcd7b8d | ||
![]() |
c68702c0a7 | ||
![]() |
98a7412855 | ||
![]() |
104880a729 | ||
![]() |
c48d4f01dc | ||
![]() |
8d5bf93360 | ||
![]() |
2f9d0a2404 | ||
![]() |
36b21948bf | ||
![]() |
4dffae3f39 | ||
![]() |
35fa6397ea | ||
![]() |
4a8887f37b | ||
![]() |
fc93762230 | ||
![]() |
ebd3f7f125 | ||
![]() |
81009c17bf | ||
![]() |
beb92e8ffb | ||
![]() |
7b4e36e990 | ||
![]() |
192e950567 | ||
![]() |
126dd45751 | ||
![]() |
ff331ffad9 | ||
![]() |
e571880c16 | ||
![]() |
e5b4d09827 | ||
![]() |
81d51fbd7e | ||
![]() |
02f9b40d67 | ||
![]() |
260a600bbc | ||
![]() |
818005fcb5 | ||
![]() |
e5f704cf3b | ||
![]() |
e2f1e38472 | ||
![]() |
b3c82d5ba2 | ||
![]() |
6d1868a6e0 | ||
![]() |
98db9331d8 | ||
![]() |
66e860a08e | ||
![]() |
3172c341e0 | ||
![]() |
8234234c48 | ||
![]() |
8d5e782fc4 | ||
![]() |
10d10d9021 | ||
![]() |
68d6d89a3b | ||
![]() |
3e73cfb71a | ||
![]() |
d7e970dcea | ||
![]() |
fb7249d1f6 | ||
![]() |
521436dd21 | ||
![]() |
c145963b02 | ||
![]() |
098ab9eae5 | ||
![]() |
a937efe719 | ||
![]() |
b7fcec7ce3 | ||
![]() |
69c23aa3ec | ||
![]() |
0a22e64799 | ||
![]() |
c3d6d69262 | ||
![]() |
7cb78ed972 | ||
![]() |
cc70f5f6a0 | ||
![]() |
85efbc6984 | ||
![]() |
3a44e8f8d3 | ||
![]() |
9bada51d56 | ||
![]() |
7bc6e9ef64 | ||
![]() |
ea797c1723 | ||
![]() |
f63d6d5b67 | ||
![]() |
8873c9a02f | ||
![]() |
ee0e131efa | ||
![]() |
af5a9d9108 | ||
![]() |
56cf9464af | ||
![]() |
9676412875 | ||
![]() |
54bea23485 | ||
![]() |
3053cbd4c8 | ||
![]() |
07069c3b1e | ||
![]() |
c0ce81ca0e | ||
![]() |
3bef456923 | ||
![]() |
91e2348381 | ||
![]() |
1564ed3256 | ||
![]() |
b8fec26115 | ||
![]() |
dd86aa9259 | ||
![]() |
84e4c15ed5 | ||
![]() |
25d1b3e1b1 | ||
![]() |
0e63efb490 | ||
![]() |
014d164d99 | ||
![]() |
fc64be6603 | ||
![]() |
9a7e48eaa6 | ||
![]() |
e050121dbf | ||
![]() |
f0a5d39625 | ||
![]() |
86f5ceb80e | ||
![]() |
06959a9ea5 | ||
![]() |
acdc66413c | ||
![]() |
816db700e1 | ||
![]() |
9030b1f89f | ||
![]() |
2e0c7abd65 | ||
![]() |
1a633f3fca | ||
![]() |
dda735ec51 | ||
![]() |
f1c98ac9e6 | ||
![]() |
7d07aaeba3 | ||
![]() |
a0163d8df0 | ||
![]() |
49ef86173f | ||
![]() |
b6c6a7e403 | ||
![]() |
672560f55b | ||
![]() |
94cbbf3c4b | ||
![]() |
40b802a5a9 | ||
![]() |
a63f027bf7 | ||
![]() |
1c02e1dadf | ||
![]() |
63b6a71ebd | ||
![]() |
0a9b632e48 | ||
![]() |
7fcc5a5417 | ||
![]() |
9cec6aaf46 | ||
![]() |
a3206bf950 | ||
![]() |
9c627920dd | ||
![]() |
8a421eb778 | ||
![]() |
14e681c954 | ||
![]() |
095a5e0ffb | ||
![]() |
b1d31a4567 | ||
![]() |
b42ca61e1f | ||
![]() |
197baf3473 | ||
![]() |
3161eb7d16 | ||
![]() |
bbbdd463fd | ||
![]() |
73ad0d468f | ||
![]() |
74d34b4f6c | ||
![]() |
bf3b38a7f2 | ||
![]() |
52d0c5fc73 | ||
![]() |
fb20381f98 | ||
![]() |
a678590ccd | ||
![]() |
bd226e9e2c | ||
![]() |
d023d5b6b4 | ||
![]() |
9b30640e67 | ||
![]() |
d17b24eea3 | ||
![]() |
d38d0b8de0 | ||
![]() |
f10b74f1e2 | ||
![]() |
5c63d8f07a | ||
![]() |
cb437829f3 | ||
![]() |
7173af60e4 | ||
![]() |
afccb37a3b | ||
![]() |
c55ef7c383 | ||
![]() |
47ea47ce14 | ||
![]() |
fd6ade2b5d | ||
![]() |
77e38abe91 | ||
![]() |
5d1011b482 | ||
![]() |
4b11e925d9 | ||
![]() |
258b98c262 | ||
![]() |
f1db257628 | ||
![]() |
3edade6761 | ||
![]() |
efcc66d63b | ||
![]() |
ca96da22d0 | ||
![]() |
85b98cf4c6 | ||
![]() |
a404fb6cb5 | ||
![]() |
3432b4625f | ||
![]() |
b8777d7739 | ||
![]() |
fb477627c7 | ||
![]() |
fd78b89c92 | ||
![]() |
45f9c52e7f | ||
![]() |
0a24ff90bb | ||
![]() |
e1eae00b35 | ||
![]() |
15bfceb05a | ||
![]() |
c1f4fe65bb | ||
![]() |
a4a6a97aa8 | ||
![]() |
608543da0b | ||
![]() |
b4fa60d4fd | ||
![]() |
b1467bd1da | ||
![]() |
3cf0f5f11b | ||
![]() |
454737ca79 | ||
![]() |
26bc889f8d | ||
![]() |
54775b896f | ||
![]() |
9217fb4094 | ||
![]() |
04d4a30471 | ||
![]() |
90f9501902 | ||
![]() |
f8d26bd865 | ||
![]() |
816d040d81 | ||
![]() |
2069293cc1 | ||
![]() |
4bd77d5899 | ||
![]() |
f8ff342852 | ||
![]() |
67ac686704 | ||
![]() |
4e5bf7ae2e | ||
![]() |
b7fd5dcb4a | ||
![]() |
bea287c5b3 | ||
![]() |
46c716d450 | ||
![]() |
9539a361e4 | ||
![]() |
ca35e5557b | ||
![]() |
a26ed3d1a6 | ||
![]() |
c7d53a5006 | ||
![]() |
41461e0d5d | ||
![]() |
c0a48d7357 | ||
![]() |
66cc744c22 | ||
![]() |
58ae734fc2 | ||
![]() |
54b2779b79 | ||
![]() |
df26e12db6 | ||
![]() |
343d89c032 | ||
![]() |
49c2d4d115 | ||
![]() |
58aefc928d | ||
![]() |
70d8902737 | ||
![]() |
78eeebf8e6 | ||
![]() |
585330b179 | ||
![]() |
5b1ac27058 | ||
![]() |
bcc36d14a1 | ||
![]() |
22f5e05060 | ||
![]() |
e510e733cd | ||
![]() |
d0a06739d8 | ||
![]() |
26c43617d1 | ||
![]() |
0a89c7ffc4 | ||
![]() |
7097cf6319 | ||
![]() |
912a13ea0d | ||
![]() |
cb391342d7 | ||
![]() |
305889f32b | ||
![]() |
f1027d7807 | ||
![]() |
2806ac6eb4 | ||
![]() |
cc1fecfffd | ||
![]() |
e02817362c | ||
![]() |
1b0484fc46 | ||
![]() |
6fe214a784 | ||
![]() |
e18a9f84a4 | ||
![]() |
59bb727636 | ||
![]() |
20e0c03b39 | ||
![]() |
6d1567cf44 | ||
![]() |
dc3f53a973 | ||
![]() |
dad7cf47b4 | ||
![]() |
165b91b068 | ||
![]() |
8211afb726 | ||
![]() |
2cccef174a | ||
![]() |
9bbef4a97b | ||
![]() |
10c2bda3a9 | ||
![]() |
cf9e04c8ec | ||
![]() |
d6887117ac | ||
![]() |
3b11be2859 | ||
![]() |
d7f52739e8 | ||
![]() |
71ea46d95e | ||
![]() |
e2afc43506 | ||
![]() |
6aed1180e7 | ||
![]() |
476b735e3c | ||
![]() |
7ad12c7f33 | ||
![]() |
60729a091a | ||
![]() |
d2bad1d553 | ||
![]() |
3e31ad51be | ||
![]() |
fbeb4664f7 | ||
![]() |
4ee8a30a5a | ||
![]() |
6243bce46c | ||
![]() |
98b72fdb9b | ||
![]() |
5e901e4d21 | ||
![]() |
66490d5db4 | ||
![]() |
2b839088c7 | ||
![]() |
28d3d3e679 | ||
![]() |
2de30e34f4 | ||
![]() |
2ff71b0d27 | ||
![]() |
cdb45364c3 | ||
![]() |
8ba338fbe1 | ||
![]() |
ce84f9c755 | ||
![]() |
d1e74a28d9 | ||
![]() |
78a2a9e666 | ||
![]() |
53f5643994 | ||
![]() |
4ee634766d | ||
![]() |
bab739efbd | ||
![]() |
8568ec838a | ||
![]() |
4cbb18aabc | ||
![]() |
3fb60aca4f | ||
![]() |
19bbdebdf7 | ||
![]() |
bc66b1a556 | ||
![]() |
4762fd83d4 | ||
![]() |
c27c12d975 | ||
![]() |
0abbd85134 | ||
![]() |
af1f00dff9 | ||
![]() |
35b4c9d375 | ||
![]() |
74da15e20d | ||
![]() |
efc7fdb669 | ||
![]() |
a75f368d5b | ||
![]() |
a3b6095b61 | ||
![]() |
7ca6f80ed2 | ||
![]() |
f1b8a7ab54 | ||
![]() |
079aa13edb | ||
![]() |
67bac9ff59 | ||
![]() |
0d80ae3a91 | ||
![]() |
b1b215f083 | ||
![]() |
f55c80eadf | ||
![]() |
c81bb2b70a | ||
![]() |
5fa9704a65 | ||
![]() |
60d39a7d1f | ||
![]() |
13564fbc17 | ||
![]() |
77a5820c3c | ||
![]() |
b790354f9a | ||
![]() |
7948819e0c | ||
![]() |
5cd13227ad | ||
![]() |
36dc7bd924 | ||
![]() |
6bd7c6c06d | ||
![]() |
e9b0840f01 | ||
![]() |
a8b01dc21a | ||
![]() |
a815592954 | ||
![]() |
f4475549d6 | ||
![]() |
a6eb227330 | ||
![]() |
343087e2b4 | ||
![]() |
66b2ad7939 | ||
![]() |
57a7103d75 | ||
![]() |
23b836ffbb | ||
![]() |
e54cf914d7 | ||
![]() |
fa57853bd2 | ||
![]() |
ddd4ec2d9e | ||
![]() |
1812e8811b | ||
![]() |
1d37d8cac0 | ||
![]() |
19da705fcb | ||
![]() |
6efc2ec9be | ||
![]() |
d0c6c7cb33 | ||
![]() |
b3b5f063cf | ||
![]() |
3731cc4334 | ||
![]() |
13df619ba9 | ||
![]() |
3edb347666 | ||
![]() |
c73832bd9c | ||
![]() |
2f26a7edae | ||
![]() |
deaf81e2a4 | ||
![]() |
f1b92718d5 | ||
![]() |
1f64649434 | ||
![]() |
ff32506c5e | ||
![]() |
68b5202730 | ||
![]() |
c6abef186c | ||
![]() |
e5bdf671b5 | ||
![]() |
88e92332ee | ||
![]() |
6da51deb83 | ||
![]() |
b44f8d52ee | ||
![]() |
fa03ed7dd7 | ||
![]() |
5617b57b26 | ||
![]() |
01210dceac | ||
![]() |
e4e049d040 | ||
![]() |
a405fba3bb | ||
![]() |
b5844db0c7 | ||
![]() |
28ab1d4551 | ||
![]() |
050ee91289 | ||
![]() |
5eb8d7e8b0 | ||
![]() |
5e4403bb2e | ||
![]() |
fb6591607f | ||
![]() |
1cf3378499 | ||
![]() |
a336aeb007 | ||
![]() |
ee49f470b7 | ||
![]() |
b9cda59172 | ||
![]() |
ba71c83948 | ||
![]() |
2835919931 | ||
![]() |
310fab526d | ||
![]() |
690b87e375 | ||
![]() |
e53625b067 | ||
![]() |
9e085c1071 | ||
![]() |
5f9dfa9493 | ||
![]() |
13051c1e5a | ||
![]() |
a9cd3609dd | ||
![]() |
e0a3e5a200 | ||
![]() |
c587fb1df8 | ||
![]() |
51cfe10c28 | ||
![]() |
bc3f95c57c | ||
![]() |
e368b9e50b | ||
![]() |
95c75c289c | ||
![]() |
23d3657ac2 | ||
![]() |
74f04336bb | ||
![]() |
54db2a48af | ||
![]() |
cde56d5a22 | ||
![]() |
3f1cf44717 | ||
![]() |
2d83ac4125 | ||
![]() |
7147486b6a | ||
![]() |
89ddbac8bc | ||
![]() |
e071b82e8a | ||
![]() |
13b2b2fc4e | ||
![]() |
fe9ef1a3ea | ||
![]() |
afb0d0f54d | ||
![]() |
26085ff82b | ||
![]() |
2872886e77 | ||
![]() |
a21112e4ab | ||
![]() |
f3edf43158 | ||
![]() |
1c5926553a | ||
![]() |
05fa3092bf | ||
![]() |
7d3ec8af37 | ||
![]() |
8db008ef0b | ||
![]() |
e493e05e99 | ||
![]() |
b83e535010 | ||
![]() |
111372edc1 | ||
![]() |
625a899f64 | ||
![]() |
aaf0496f74 | ||
![]() |
4977926c88 | ||
![]() |
f41e1159d1 | ||
![]() |
670107373b | ||
![]() |
e660f05c31 | ||
![]() |
baf1ea313e | ||
![]() |
ed64c91da6 | ||
![]() |
c40aa4399b | ||
![]() |
8f08100a30 | ||
![]() |
337cd33042 | ||
![]() |
1e8fc7266c | ||
![]() |
7f35583c2c | ||
![]() |
ace755f264 | ||
![]() |
7b25c9d0a7 | ||
![]() |
c0bee2a6b7 | ||
![]() |
b48d5cab22 | ||
![]() |
4f59e6c7ab | ||
![]() |
82a5d54d2c | ||
![]() |
5e6d830ecd | ||
![]() |
f700f3427b | ||
![]() |
0c07c0ba4e | ||
![]() |
bc885f3644 | ||
![]() |
6668964d92 | ||
![]() |
1835fbae49 | ||
![]() |
593489a14c | ||
![]() |
9f7bf36786 | ||
![]() |
f0302670d2 | ||
![]() |
4b8cc7b533 | ||
![]() |
6e953ff5eb | ||
![]() |
7316ad5a72 | ||
![]() |
f28fc8fa5c | ||
![]() |
02b70e693c | ||
![]() |
b2e06477f8 | ||
![]() |
632971a2ac | ||
![]() |
8045fd3f14 | ||
![]() |
a2568f711f | ||
![]() |
f9032866e7 | ||
![]() |
e287b18435 | ||
![]() |
c415ee82d1 | ||
![]() |
c8f1a15f21 | ||
![]() |
9012cf6946 | ||
![]() |
7595d01956 | ||
![]() |
ed3c239b7e | ||
![]() |
c254a04aec | ||
![]() |
d5b96c0257 | ||
![]() |
436a2e9bf3 | ||
![]() |
b34f4345e1 | ||
![]() |
f55d63fae8 | ||
![]() |
ed594c1987 | ||
![]() |
ab85dd9fa8 | ||
![]() |
08c7054845 | ||
![]() |
9ef41bf1c7 | ||
![]() |
1064128fde | ||
![]() |
382341f550 | ||
![]() |
81e07fda08 | ||
![]() |
4c4435bc19 | ||
![]() |
f952bc0b64 | ||
![]() |
05e1a6d949 | ||
![]() |
ea3d01ec62 | ||
![]() |
2d4e2af629 | ||
![]() |
f18c2fd339 | ||
![]() |
cd184cf366 | ||
![]() |
6387e38e27 | ||
![]() |
2fb85f4a16 | ||
![]() |
34d1f74b77 | ||
![]() |
48c9cfb432 | ||
![]() |
f9739c9730 | ||
![]() |
863e983726 | ||
![]() |
b71d7e33bb | ||
![]() |
93462aafbc | ||
![]() |
ea64fdd7b4 | ||
![]() |
c86b2ae500 | ||
![]() |
848ba685eb | ||
![]() |
9ad024c189 | ||
![]() |
0b15f6035b | ||
![]() |
1e7b657156 | ||
![]() |
6180828ed2 | ||
![]() |
785f61ba70 | ||
![]() |
50f26374e3 | ||
![]() |
398bd04ffd | ||
![]() |
8349a28ed8 | ||
![]() |
27018e4ab6 | ||
![]() |
73e82303e7 | ||
![]() |
64697235d6 | ||
![]() |
a5cc408469 | ||
![]() |
d590dec159 | ||
![]() |
b262bcec03 | ||
![]() |
fe2330ebf6 | ||
![]() |
50c7b35291 | ||
![]() |
927d6ab1c6 | ||
![]() |
d064477a45 | ||
![]() |
6588bb3d79 | ||
![]() |
812cb3d940 | ||
![]() |
852ef3cd1b | ||
![]() |
3cc77d945b | ||
![]() |
6f4449d5e9 | ||
![]() |
37edef834e | ||
![]() |
814030be77 | ||
![]() |
71a2914f3e | ||
![]() |
0d30ceb284 | ||
![]() |
4add6cb26e | ||
![]() |
8a3ab5be3e | ||
![]() |
8e18acff85 | ||
![]() |
8fd4edb206 | ||
![]() |
2099b04057 | ||
![]() |
1a0a3aa2c1 | ||
![]() |
7947f4db4c | ||
![]() |
1df068bac9 | ||
![]() |
d9e084706f | ||
![]() |
55e7893bad | ||
![]() |
604b10778c | ||
![]() |
d69fa3ceae | ||
![]() |
f55b3add80 | ||
![]() |
7c2f7d6c51 | ||
![]() |
19cc94e594 | ||
![]() |
b93bbc9f5d | ||
![]() |
2feac54382 | ||
![]() |
49f1f6cad7 | ||
![]() |
399312ead3 | ||
![]() |
f9671dfbf7 | ||
![]() |
b1fcf02d13 | ||
![]() |
615893be38 | ||
![]() |
5869648f19 | ||
![]() |
734f8e02b5 | ||
![]() |
e477f99c7d | ||
![]() |
455a36b0fc | ||
![]() |
ad343b7b32 | ||
![]() |
df9c05bef3 | ||
![]() |
6c8c16c85f | ||
![]() |
b05f3fd266 | ||
![]() |
ca98d73d86 | ||
![]() |
b7ae3be394 | ||
![]() |
621fa5ba54 | ||
![]() |
ca1b9bf7b3 | ||
![]() |
6fa685d9d8 | ||
![]() |
ff26d3666e | ||
![]() |
e3557fd80e | ||
![]() |
c065705608 | ||
![]() |
3948247055 | ||
![]() |
dca48d7722 | ||
![]() |
8e6c90e294 | ||
![]() |
e5908f2508 | ||
![]() |
fbd98ec0f9 | ||
![]() |
1ab05e8de0 | ||
![]() |
86562f256f | ||
![]() |
add5219d34 | ||
![]() |
6ae5d11ec0 | ||
![]() |
b4e641548c | ||
![]() |
017214fd56 | ||
![]() |
22a73b67d3 | ||
![]() |
792ecc6cac | ||
![]() |
e98398cab8 | ||
![]() |
df1e8679d9 | ||
![]() |
5e3bdc76b2 | ||
![]() |
47982641b2 | ||
![]() |
39a885a37c | ||
![]() |
0e8d235148 | ||
![]() |
053a5235be | ||
![]() |
de42ebf3d8 | ||
![]() |
4d3ce0a65e | ||
![]() |
b3e97a1a0c | ||
![]() |
f5d9826b12 | ||
![]() |
61e5e65173 | ||
![]() |
b258f3552a | ||
![]() |
e803bc909f | ||
![]() |
d078aea32b | ||
![]() |
fb2cfcb640 | ||
![]() |
99f85fb359 | ||
![]() |
454fb106d2 | ||
![]() |
7d078a2f0e | ||
![]() |
b015648bfe | ||
![]() |
a58482cb2b | ||
![]() |
9dd1d81536 | ||
![]() |
a2f5674bbb | ||
![]() |
837ad24f58 | ||
![]() |
058c62b111 | ||
![]() |
bbb6bca605 | ||
![]() |
a8e5a1de15 | ||
![]() |
bba4c44182 | ||
![]() |
7c76249e1f | ||
![]() |
294955db17 | ||
![]() |
752ad2d2eb | ||
![]() |
02a268c7c6 | ||
![]() |
b2dc7adf3b | ||
![]() |
6e62558d81 | ||
![]() |
0d0866d5d9 | ||
![]() |
00f65a53dd | ||
![]() |
751922990f | ||
![]() |
4311d385fc | ||
![]() |
3e2f335a4c | ||
![]() |
cf1eddb449 | ||
![]() |
e171fec5aa | ||
![]() |
7f44d508dc | ||
![]() |
2c924e4c1c | ||
![]() |
0f0375a67e | ||
![]() |
069c68bfe4 | ||
![]() |
c03d8e312a | ||
![]() |
de7f66f983 | ||
![]() |
82b89aa20b | ||
![]() |
80d02e8a8d | ||
![]() |
868f629f32 | ||
![]() |
746ca5d5ed | ||
![]() |
3c5fefde2e | ||
![]() |
26f58d3335 | ||
![]() |
6baeca654b | ||
![]() |
1b15b5414c | ||
![]() |
48e4ea5231 | ||
![]() |
f9fbf1a2a5 | ||
![]() |
f003ff3c98 | ||
![]() |
81e2b18531 | ||
![]() |
c404ea20ee | ||
![]() |
cc45564d84 | ||
![]() |
8d560ec55f | ||
![]() |
df74111427 | ||
![]() |
93c35efe67 | ||
![]() |
296c77ac73 | ||
![]() |
9c0f444e4d | ||
![]() |
6b0f91cafd | ||
![]() |
3f71d2d33d | ||
![]() |
f2942588f2 | ||
![]() |
b47027efc2 | ||
![]() |
34201be74c | ||
![]() |
3e804f16df | ||
![]() |
3512140148 | ||
![]() |
bff6914a73 | ||
![]() |
652add635f | ||
![]() |
fde410e2ac | ||
![]() |
f04e47803c | ||
![]() |
61d74263d9 | ||
![]() |
66ee065c0c | ||
![]() |
09bcf6974e | ||
![]() |
5d7d615433 | ||
![]() |
5387048dc3 | ||
![]() |
6930df71cf | ||
![]() |
52bbf6da5d | ||
![]() |
1cd5df7558 | ||
![]() |
74429798e2 | ||
![]() |
651f3ea5eb | ||
![]() |
0909335d02 | ||
![]() |
827e4b5f75 | ||
![]() |
c8ff07fff0 | ||
![]() |
4a21cb2d00 | ||
![]() |
07f7fffae7 | ||
![]() |
441ee2ef90 | ||
![]() |
acad133e3a | ||
![]() |
ef8714fda9 | ||
![]() |
16171eee8d | ||
![]() |
d3c1781478 | ||
![]() |
329b52e670 | ||
![]() |
a1b9a1d244 | ||
![]() |
377cec9fb1 | ||
![]() |
48b9c63268 | ||
![]() |
caccb1094d | ||
![]() |
43ffcf7e8f | ||
![]() |
77fe2e55be | ||
![]() |
a59e9e1d9e | ||
![]() |
896645130b | ||
![]() |
045bb855d2 | ||
![]() |
3b4f6edbdb | ||
![]() |
1cbf9ff621 | ||
![]() |
41c2c8b82d | ||
![]() |
43ec0b77a0 | ||
![]() |
408fa45c51 | ||
![]() |
eed1243263 | ||
![]() |
8f5214724c | ||
![]() |
55b6b28afb | ||
![]() |
5a48034e33 | ||
![]() |
756f4e5986 | ||
![]() |
48492b9f4e | ||
![]() |
e101e40c47 | ||
![]() |
9a80a2151c | ||
![]() |
73075c64d1 | ||
![]() |
053a0482b4 | ||
![]() |
9cdec62918 | ||
![]() |
e3694695ae | ||
![]() |
9a3a01ca78 | ||
![]() |
f0bc318712 | ||
![]() |
53adb0c515 | ||
![]() |
747afa0cee | ||
![]() |
104e489000 | ||
![]() |
5764bf16f3 | ||
![]() |
8ebac41318 | ||
![]() |
a2130aa6c5 | ||
![]() |
5dbf46ac3c | ||
![]() |
b7d42e7e8e | ||
![]() |
d08535e7f6 | ||
![]() |
eb1225a0a5 | ||
![]() |
284edd97d6 | ||
![]() |
d1b0b64d59 | ||
![]() |
d0cc231782 | ||
![]() |
6ce35d47f5 | ||
![]() |
d1db479727 | ||
![]() |
1e748864c5 | ||
![]() |
c92c442356 | ||
![]() |
1f4993350a | ||
![]() |
f9b1d1edaf | ||
![]() |
cab5477656 | ||
![]() |
b8de668f5f | ||
![]() |
c5234731d6 | ||
![]() |
ef86a77946 | ||
![]() |
1b301984dd | ||
![]() |
9807f76aff | ||
![]() |
47673dd773 | ||
![]() |
a9fb1d435a | ||
![]() |
422ad20641 | ||
![]() |
3ea2fe1c48 | ||
![]() |
038e064e60 | ||
![]() |
800f010383 | ||
![]() |
4350f9363d | ||
![]() |
76a1629e75 | ||
![]() |
2493dfaba3 | ||
![]() |
656dc08406 | ||
![]() |
631f13cf2f | ||
![]() |
9730bf0acc | ||
![]() |
9f2b5ea86e | ||
![]() |
5702442783 | ||
![]() |
74c2f446e9 | ||
![]() |
da1710bcd2 | ||
![]() |
2dfd56b49b | ||
![]() |
6538e599dd | ||
![]() |
789e3e3924 | ||
![]() |
3d505e425d | ||
![]() |
e7122d7a72 | ||
![]() |
94d0705607 | ||
![]() |
caba462703 | ||
![]() |
ffe397247e | ||
![]() |
e7ad622c02 | ||
![]() |
bca4626708 | ||
![]() |
7f0ad8e2d2 | ||
![]() |
6c6c5ef651 | ||
![]() |
a460940430 | ||
![]() |
fc2455be80 | ||
![]() |
fd4357cf23 | ||
![]() |
e41e0df27e | ||
![]() |
f370dc3929 | ||
![]() |
1c2d83e2c7 | ||
![]() |
d6756f3d81 | ||
![]() |
71ef7685c5 | ||
![]() |
b7516f31c6 | ||
![]() |
065fb166c2 | ||
![]() |
4cc6e3b966 | ||
![]() |
1c293a2759 | ||
![]() |
062e2eca6f | ||
![]() |
bcc2c34eef | ||
![]() |
1613ae9185 | ||
![]() |
d827a6182b | ||
![]() |
83df14d379 | ||
![]() |
7c1dae918d | ||
![]() |
1b54c4f8e7 | ||
![]() |
49b74e9091 | ||
![]() |
a1f1e5bc37 | ||
![]() |
2dc8a93685 | ||
![]() |
c2145cbe11 | ||
![]() |
50a792a81a | ||
![]() |
e2bd7e1e08 | ||
![]() |
11a5a990d0 | ||
![]() |
ecc894ac82 | ||
![]() |
50b649cd3e | ||
![]() |
99b018cd49 | ||
![]() |
6aa2800275 | ||
![]() |
cd7fc7e026 | ||
![]() |
b4d312efb6 | ||
![]() |
e9722710ac | ||
![]() |
f1384fea58 | ||
![]() |
feadc45e75 | ||
![]() |
eefe5266a8 | ||
![]() |
74353193f8 | ||
![]() |
0ccb73cf2b | ||
![]() |
356f4424df | ||
![]() |
85c6cf4309 | ||
![]() |
96fb68135e | ||
![]() |
a7b9adc692 | ||
![]() |
e028cf9002 | ||
![]() |
f984be8ea0 | ||
![]() |
3d426b55d3 | ||
![]() |
02b8b2c125 | ||
![]() |
dc7b0f75bb | ||
![]() |
a089d9891d | ||
![]() |
a1183f4b4b | ||
![]() |
84cfa38510 | ||
![]() |
89edbcacfa | ||
![]() |
c8e649f190 | ||
![]() |
790e43dd6e | ||
![]() |
59f6b2ff2e | ||
![]() |
829defbf61 | ||
![]() |
70a0f4ae48 | ||
![]() |
c7c0ef6abc | ||
![]() |
2fc8a0db92 | ||
![]() |
b50c621be8 | ||
![]() |
126f5857c3 | ||
![]() |
8b3e1764a8 | ||
![]() |
b776461297 | ||
![]() |
4a0052026f | ||
![]() |
35c4887e4a | ||
![]() |
f5b87833f8 | ||
![]() |
0dde76bbbc | ||
![]() |
93863b0629 | ||
![]() |
115a47d4c6 | ||
![]() |
308c63df16 | ||
![]() |
ab86d0a18d | ||
![]() |
3ec74444b0 | ||
![]() |
1979c84ea8 | ||
![]() |
f4aefcb18b | ||
![]() |
7f2fa23179 | ||
![]() |
4524aa0d06 | ||
![]() |
15fa8250cb | ||
![]() |
4dff129949 | ||
![]() |
43951ec208 | ||
![]() |
f961acdf0c | ||
![]() |
2c7821e5e6 | ||
![]() |
d25ddfc46b | ||
![]() |
8cc9b08c06 | ||
![]() |
98b9d815a6 | ||
![]() |
a808b9403e | ||
![]() |
c956eee919 | ||
![]() |
aa97ca9ccf | ||
![]() |
98bb3de8da | ||
![]() |
cd43edf074 | ||
![]() |
dffd992304 | ||
![]() |
f1b70e13a1 | ||
![]() |
d91247dc35 | ||
![]() |
25f55ee6bb | ||
![]() |
c2e9fe0aac | ||
![]() |
5885ec8e65 | ||
![]() |
053104fc50 | ||
![]() |
861de7f8b3 | ||
![]() |
b5a4aef829 | ||
![]() |
54b4f8afbd | ||
![]() |
65daf342df | ||
![]() |
15a498fd60 | ||
![]() |
af7da9d2c9 | ||
![]() |
91ad584064 | ||
![]() |
b6b9f51bd7 | ||
![]() |
6acfb55dcc | ||
![]() |
ce42b84430 | ||
![]() |
59d93138d3 | ||
![]() |
78de189d56 | ||
![]() |
b21c99eb12 | ||
![]() |
e22cdea485 | ||
![]() |
1e97407025 | ||
![]() |
c4f5dc6d01 | ||
![]() |
c329a17975 | ||
![]() |
2a88cc74bf | ||
![]() |
7e965cb6d4 | ||
![]() |
6631b286c1 | ||
![]() |
b8313abfa8 | ||
![]() |
aa91b946fa | ||
![]() |
82af2c5717 | ||
![]() |
4cdc59e51c | ||
![]() |
d34585e4b0 | ||
![]() |
d565a684a1 | ||
![]() |
13f178dca8 | ||
![]() |
f8ba33e81c | ||
![]() |
3d251f51fc | ||
![]() |
57704522cd | ||
![]() |
787926c111 | ||
![]() |
08b424b3df | ||
![]() |
736a946101 | ||
![]() |
6f6f847ee2 | ||
![]() |
13be271df7 | ||
![]() |
b423852fad | ||
![]() |
fe3d6b870a | ||
![]() |
14be63039f | ||
![]() |
d339d4c8dd | ||
![]() |
b0d5cb62fa | ||
![]() |
975d23ee5c | ||
![]() |
8a421831ab | ||
![]() |
c8d3faec6d | ||
![]() |
8a45c258c5 | ||
![]() |
d45ff72c9c | ||
![]() |
137d246d6a | ||
![]() |
e80d37bf8f | ||
![]() |
b970a40b4e | ||
![]() |
1e32a5fffd | ||
![]() |
2e5cd986dd | ||
![]() |
635eee9e5e | ||
![]() |
ae3ea9e531 | ||
![]() |
5b241f0b64 | ||
![]() |
1a64075027 | ||
![]() |
100866be37 | ||
![]() |
2179530084 | ||
![]() |
d500ef77cf | ||
![]() |
4952b3a2d6 | ||
![]() |
a9859bc029 | ||
![]() |
561b208508 | ||
![]() |
de59d02ad7 | ||
![]() |
017a34fc10 | ||
![]() |
d314805caf | ||
![]() |
eb9481b668 | ||
![]() |
1564807aa0 | ||
![]() |
dd8d113334 | ||
![]() |
258bc328e0 | ||
![]() |
c0de3aa35c | ||
![]() |
a1a62b00a0 | ||
![]() |
db628cec11 | ||
![]() |
6f7071b12d | ||
![]() |
e9c171f7ab | ||
![]() |
983abf5e14 | ||
![]() |
91e27affeb | ||
![]() |
d76b3c8f78 | ||
![]() |
fb42a736f1 | ||
![]() |
a68fbcc520 | ||
![]() |
9fc70fc24e | ||
![]() |
1f17720be2 | ||
![]() |
ab5b92ae68 | ||
![]() |
767410959a | ||
![]() |
e3b043e0e1 | ||
![]() |
0979906933 | ||
![]() |
e241fd0418 | ||
![]() |
8e3a7caebd | ||
![]() |
b03ce897c7 | ||
![]() |
d7c1005a50 | ||
![]() |
1111c15f77 | ||
![]() |
fc12a9f751 | ||
![]() |
cfcae39699 | ||
![]() |
4c923bae7d | ||
![]() |
a5a6bebf0b | ||
![]() |
6f1d0a3caa | ||
![]() |
2b5484539d | ||
![]() |
5d21dc95ea | ||
![]() |
e32b6c98df | ||
![]() |
7b9248c10a | ||
![]() |
4853240de9 | ||
![]() |
d5f2e3e45c | ||
![]() |
ad680b6a35 | ||
![]() |
4cb74f0fe4 | ||
![]() |
333ab1124b | ||
![]() |
48393c215b | ||
![]() |
ec6a7ae97c | ||
![]() |
808d6423be | ||
![]() |
9076f3e69e | ||
![]() |
7e526f87b4 | ||
![]() |
fc585bffcc | ||
![]() |
d6f2ca6aaa | ||
![]() |
2dcccb37a0 | ||
![]() |
c584791b65 | ||
![]() |
ed551500e7 | ||
![]() |
94b2ea9b5f | ||
![]() |
8b001b87d2 | ||
![]() |
b06ddec2d5 | ||
![]() |
d04f340b5b | ||
![]() |
aaaf1a6cf8 | ||
![]() |
23e4449f27 | ||
![]() |
51785a1ead | ||
![]() |
49f66be8af | ||
![]() |
009b6e3ca5 | ||
![]() |
c011b06bea | ||
![]() |
34d300d1da | ||
![]() |
468e620372 | ||
![]() |
e05153d7bb | ||
![]() |
4aa4a3b597 | ||
![]() |
b1d17302bc | ||
![]() |
cc3ffcbb84 | ||
![]() |
eda9e580c9 | ||
![]() |
76a07a3ebc | ||
![]() |
abe87686a2 | ||
![]() |
6371c11fc5 | ||
![]() |
d5596cf6a2 | ||
![]() |
2f64af9cb2 | ||
![]() |
b0d5c7035b | ||
![]() |
0c61521521 | ||
![]() |
3497a0de54 | ||
![]() |
117f2fa00d | ||
![]() |
2c67090e3c | ||
![]() |
10ccbeab35 | ||
![]() |
b49f66bbc9 | ||
![]() |
9adbbd42be | ||
![]() |
8563bd463c | ||
![]() |
da5a6d2272 | ||
![]() |
0854737be2 | ||
![]() |
6f3f8b0a48 | ||
![]() |
f0e272d0f2 | ||
![]() |
5e207aa7c1 | ||
![]() |
e0b80f49b6 | ||
![]() |
97bbe42599 | ||
![]() |
089dbdbd7e | ||
![]() |
833c099025 | ||
![]() |
4e526dfaae | ||
![]() |
cae37657e9 | ||
![]() |
1a94530935 | ||
![]() |
75d28d3c58 | ||
![]() |
cd59f7aad6 | ||
![]() |
2f9fcd96c7 | ||
![]() |
c74fba483d | ||
![]() |
193dd01e06 | ||
![]() |
2400004f41 | ||
![]() |
b862c20e8e | ||
![]() |
c0ed623d26 | ||
![]() |
501b96baf7 | ||
![]() |
d2600e0ddd | ||
![]() |
7d799b785e | ||
![]() |
e36b620020 | ||
![]() |
1efc74dabc | ||
![]() |
54f98053a8 | ||
![]() |
6745826f35 | ||
![]() |
bbd897b8ff | ||
![]() |
586590e9ec | ||
![]() |
4bf50a0b46 | ||
![]() |
40832f0ea7 | ||
![]() |
32a065afc7 | ||
![]() |
4dafc74223 | ||
![]() |
8adf1231a3 | ||
![]() |
c00624f209 | ||
![]() |
eccde8fa07 | ||
![]() |
0616a66b05 | ||
![]() |
67453d18ff | ||
![]() |
792a87e407 | ||
![]() |
6da50626e1 | ||
![]() |
6239b3b309 | ||
![]() |
b9bc621e2a | ||
![]() |
e10bbfa933 | ||
![]() |
2dd301e292 | ||
![]() |
75edc6de0f | ||
![]() |
780c5183e3 | ||
![]() |
25a10784eb | ||
![]() |
6e1d09fc32 | ||
![]() |
73a2063d96 | ||
![]() |
deb1e7f41f | ||
![]() |
f45f719b9d | ||
![]() |
325639b308 | ||
![]() |
386eef046d | ||
![]() |
db6b14361d | ||
![]() |
719f074ccf | ||
![]() |
646b912da8 | ||
![]() |
b29c43d86a | ||
![]() |
7ce64ecf05 | ||
![]() |
9a332074c7 | ||
![]() |
dd02f1025f | ||
![]() |
d7bfab7b13 | ||
![]() |
05cf5d57a9 | ||
![]() |
f56eaae019 | ||
![]() |
0d436db3ea | ||
![]() |
6c8b29f326 | ||
![]() |
23e76b0bd9 | ||
![]() |
82e8cd0f8d | ||
![]() |
87d84b922f | ||
![]() |
04955a4123 | ||
![]() |
54831878e0 | ||
![]() |
08ed71e51e | ||
![]() |
3a1d5de742 | ||
![]() |
e15be5bf9a | ||
![]() |
01afeefeb9 | ||
![]() |
532bd6fe12 | ||
![]() |
416e30ede2 | ||
![]() |
ceb81d00fc | ||
![]() |
8adca31c24 | ||
![]() |
3cce43309c | ||
![]() |
63ad802013 | ||
![]() |
9313e70575 | ||
![]() |
838ea56605 | ||
![]() |
9ac087c59c | ||
![]() |
f52e076cb3 | ||
![]() |
8857d0b8df | ||
![]() |
950989a85e | ||
![]() |
a4c215751e | ||
![]() |
2ca560ebf8 | ||
![]() |
1f631eafce | ||
![]() |
6f605d4a35 | ||
![]() |
1918625be9 | ||
![]() |
bdf35b6688 | ||
![]() |
2ac54ce4bd | ||
![]() |
96d75c9ad4 | ||
![]() |
dac4020f27 | ||
![]() |
a5f49b065c | ||
![]() |
d5d0624311 | ||
![]() |
8708867c1c | ||
![]() |
8f11529a75 | ||
![]() |
0aaeab124d | ||
![]() |
1cc184ed10 | ||
![]() |
830f4268c3 | ||
![]() |
977740045a | ||
![]() |
2a1dcbc28b | ||
![]() |
21f8ab647f | ||
![]() |
aef5a48fc6 | ||
![]() |
434c1a0f20 | ||
![]() |
5fd2496774 | ||
![]() |
7411bcbb30 | ||
![]() |
7d6d51f4a5 | ||
![]() |
5777693fad | ||
![]() |
b53cc4f9db | ||
![]() |
9d57039274 | ||
![]() |
12217bde8a | ||
![]() |
37f802d1fe | ||
![]() |
df1710f4cc | ||
![]() |
0fec34d316 | ||
![]() |
a0b8312ce4 | ||
![]() |
8abe6909ca | ||
![]() |
25cff6a748 | ||
![]() |
243c98a02e | ||
![]() |
c9a6820de7 | ||
![]() |
7d586492f3 | ||
![]() |
807bdfeda9 | ||
![]() |
1f25df308a | ||
![]() |
2efa8b6960 | ||
![]() |
7c9d2018d8 | ||
![]() |
ab90b01122 | ||
![]() |
d04ef319b8 | ||
![]() |
368142e79b | ||
![]() |
7d45ae68a6 | ||
![]() |
98bedcf1e5 | ||
![]() |
3377fa4640 | ||
![]() |
641c05c6fe | ||
![]() |
e157a69d86 | ||
![]() |
3d468c369c | ||
![]() |
6c7679714b | ||
![]() |
71d8567f18 | ||
![]() |
cc6253ba38 | ||
![]() |
3ea107be5a | ||
![]() |
4ed96cf1bd | ||
![]() |
9323cc76d9 | ||
![]() |
da9b9c8c69 | ||
![]() |
3c5c0ea68f | ||
![]() |
2b988e1d5d | ||
![]() |
8bcb2558b6 | ||
![]() |
b8785a5b93 | ||
![]() |
b00631d186 | ||
![]() |
de5a6b2c35 | ||
![]() |
3beb8193ae | ||
![]() |
a2549c5bbd | ||
![]() |
98a8be82e2 | ||
![]() |
5c86e13239 | ||
![]() |
10cb612fb1 | ||
![]() |
a9a769d902 | ||
![]() |
846e35f57e | ||
![]() |
a3b9a0be3a | ||
![]() |
2a3235f606 | ||
![]() |
08b221c270 | ||
![]() |
3102c3128f | ||
![]() |
9ebed3c1b4 | ||
![]() |
24d672a0ff | ||
![]() |
e9f99302c1 | ||
![]() |
5cdf7671ed | ||
![]() |
4dab50c10a | ||
![]() |
c416dd30e2 | ||
![]() |
4ebc8870c2 | ||
![]() |
bf3f4e560d | ||
![]() |
4be55428d2 | ||
![]() |
e9c9b7a3e2 | ||
![]() |
2d2cfb0349 | ||
![]() |
98998cccbc | ||
![]() |
03d484aba2 | ||
![]() |
88a2966666 | ||
![]() |
e408e8ca4a | ||
![]() |
9bfb4dfd06 | ||
![]() |
7dc7281e69 | ||
![]() |
87fea29e32 | ||
![]() |
2cf42e867c | ||
![]() |
83a2669ff5 | ||
![]() |
824409351e | ||
![]() |
d1ea6a897e | ||
![]() |
5d3e8f17d1 | ||
![]() |
78a5fe2d37 | ||
![]() |
5ad4e5b614 | ||
![]() |
000d0a08f4 | ||
![]() |
917f1dea9f | ||
![]() |
e309647f1b | ||
![]() |
57136e48fb | ||
![]() |
8c315dfeb1 | ||
![]() |
6e9749d6c4 | ||
![]() |
bf6f94f69f | ||
![]() |
575154fdea | ||
![]() |
857bbe3c3b | ||
![]() |
0a0b255505 | ||
![]() |
73b4b032b1 | ||
![]() |
8234e44921 | ||
![]() |
36197cca98 | ||
![]() |
7a25d359b7 | ||
![]() |
7cfb257c00 | ||
![]() |
b660240059 | ||
![]() |
125ec1e85f | ||
![]() |
d31b35873f | ||
![]() |
e1c520b9e7 | ||
![]() |
1361f18964 | ||
![]() |
0f00f22212 | ||
![]() |
0d543bbb0a | ||
![]() |
86b3bdb90b | ||
![]() |
d47cdfb647 | ||
![]() |
ac5c17e8be | ||
![]() |
db67093391 | ||
![]() |
318fba6c97 | ||
![]() |
2d63fa80b4 | ||
![]() |
1dc211a046 | ||
![]() |
f71f379529 | ||
![]() |
bee95b4977 | ||
![]() |
9f8aaa57b6 | ||
![]() |
2c1aab154a | ||
![]() |
37cfac27b8 | ||
![]() |
11b2e2a6e2 | ||
![]() |
d555ee737b | ||
![]() |
12a6a7d95a | ||
![]() |
caac3bfc95 | ||
![]() |
05630776a0 | ||
![]() |
72c947cbaf | ||
![]() |
53fb3a36f7 | ||
![]() |
6b3892987a | ||
![]() |
390919c439 | ||
![]() |
09ab06ae6c | ||
![]() |
ad9373312b | ||
![]() |
bd71e087d4 | ||
![]() |
c90dcde7cc | ||
![]() |
d91cc3616b | ||
![]() |
74cd3d66c6 | ||
![]() |
e6f9d9a31a | ||
![]() |
b71a86142b | ||
![]() |
6e4ba6184b | ||
![]() |
b37162099e | ||
![]() |
dab74662e9 | ||
![]() |
3d103046bc | ||
![]() |
3ca62d9c55 | ||
![]() |
2cd45ed1de | ||
![]() |
263598f2cf | ||
![]() |
1d1d71c779 | ||
![]() |
fd13265131 | ||
![]() |
911c35a7f1 | ||
![]() |
adb265794c | ||
![]() |
8c20d8cb3d | ||
![]() |
43359f1d26 | ||
![]() |
dc9da7480c | ||
![]() |
18647203cc | ||
![]() |
be8f2c01a2 | ||
![]() |
c8fffe4ade | ||
![]() |
43fd7737f1 | ||
![]() |
fb4969d5d1 | ||
![]() |
3cc4af5947 | ||
![]() |
ac39ebddc0 | ||
![]() |
29bb1f7ef2 | ||
![]() |
b8d2f5b373 | ||
![]() |
2139853dd9 | ||
![]() |
527aa61a87 | ||
![]() |
4261fc8a04 | ||
![]() |
7dbddba757 | ||
![]() |
16183791f3 | ||
![]() |
f38c7a4b7e | ||
![]() |
bb0b2a1f53 | ||
![]() |
0048662182 | ||
![]() |
7bd2455175 | ||
![]() |
43e49f36b7 | ||
![]() |
e4b3479779 | ||
![]() |
6bac9c7e8f | ||
![]() |
ff3cde4dfb | ||
![]() |
7aab84f2d9 | ||
![]() |
3a940711eb | ||
![]() |
2b0b2bb1ae | ||
![]() |
e39507552f | ||
![]() |
b019ab79f9 | ||
![]() |
43da8c2a72 | ||
![]() |
0b65cea6fd | ||
![]() |
a1806390b0 | ||
![]() |
5d6559e839 | ||
![]() |
29c79ad1d8 | ||
![]() |
d77a1aba7a | ||
![]() |
9e21b16553 | ||
![]() |
dcb56ae775 | ||
![]() |
ab2c019a7a | ||
![]() |
eb408d4858 | ||
![]() |
4f38851880 | ||
![]() |
2c356ec87f | ||
![]() |
bb84464216 | ||
![]() |
32b9e0bad4 | ||
![]() |
02f5a86ee9 | ||
![]() |
391bf052e4 | ||
![]() |
d2a9363fc5 | ||
![]() |
68af4cd5ba | ||
![]() |
c82dcb11e1 | ||
![]() |
d0f8d8d1f9 | ||
![]() |
6a852332de | ||
![]() |
830fec0c29 | ||
![]() |
aa68d35f42 | ||
![]() |
6e6fe9bc87 | ||
![]() |
29f68e6dbb | ||
![]() |
9428b2576b | ||
![]() |
3210302ecd | ||
![]() |
f23979024a | ||
![]() |
870a65fa6d | ||
![]() |
199eb20b66 | ||
![]() |
91114e5aa0 | ||
![]() |
dfbc831525 | ||
![]() |
1a640609c7 | ||
![]() |
00630bd4a3 | ||
![]() |
fb408d7aa3 | ||
![]() |
6b5d6e4091 | ||
![]() |
a287be1f0c | ||
![]() |
42a3149fe3 | ||
![]() |
5aee5c0fb8 | ||
![]() |
12ecf366b0 | ||
![]() |
275562bce0 | ||
![]() |
a09030fd6d | ||
![]() |
414893a687 | ||
![]() |
5939d79057 | ||
![]() |
189bd37e71 | ||
![]() |
715056047c | ||
![]() |
0220f900c1 | ||
![]() |
10a0e58572 | ||
![]() |
8d47798fa2 | ||
![]() |
3f2513a717 | ||
![]() |
9be71f603e | ||
![]() |
d354b38139 | ||
![]() |
1152cd4f07 | ||
![]() |
de0e218440 | ||
![]() |
d377cf0d02 | ||
![]() |
788b435f9b | ||
![]() |
6ea91b2dde | ||
![]() |
55d883925f | ||
![]() |
89aff7764d | ||
![]() |
c4e1bc35b4 | ||
![]() |
7e53e33e0f | ||
![]() |
8b73c2bf8a | ||
![]() |
bcb0056b55 | ||
![]() |
b1311547b2 | ||
![]() |
bddba4bd96 | ||
![]() |
8f304b8157 | ||
![]() |
6acfac9064 | ||
![]() |
f64db3a2f9 | ||
![]() |
d1db47ee34 | ||
![]() |
3b1f27b674 | ||
![]() |
0e753b245a | ||
![]() |
8b7d7f1666 | ||
![]() |
0f1afff4c3 | ||
![]() |
50c36068e7 | ||
![]() |
a6f7fdba4e | ||
![]() |
9d337bf4dc | ||
![]() |
b7d34079d9 | ||
![]() |
eade36ee82 | ||
![]() |
443d08381a | ||
![]() |
4e6880e520 | ||
![]() |
9a300d0286 | ||
![]() |
9987e3bcef | ||
![]() |
cc749858cb | ||
![]() |
89264b3da4 | ||
![]() |
3aab8ccb4a | ||
![]() |
171ba84741 | ||
![]() |
83271bb11e | ||
![]() |
ffbc9a28ad | ||
![]() |
a65fea4d64 | ||
![]() |
182ee3c0da | ||
![]() |
efe204fef8 | ||
![]() |
026308acc9 | ||
![]() |
3f60cf5377 | ||
![]() |
b07891089f | ||
![]() |
4cfac47674 | ||
![]() |
f4c90426a5 | ||
![]() |
e5d798581c | ||
![]() |
05b79eb77b | ||
![]() |
4e0fe27de3 | ||
![]() |
8eb82836b9 | ||
![]() |
5262e92b9f | ||
![]() |
c0a6b3d5a3 | ||
![]() |
66cd7dd809 | ||
![]() |
c90a88fb17 | ||
![]() |
de4a699c46 | ||
![]() |
149d0da724 | ||
![]() |
5340683199 | ||
![]() |
652f5cbf20 | ||
![]() |
8094b25185 | ||
![]() |
bdad18a572 | ||
![]() |
a8cbda5f24 | ||
![]() |
753d81adad | ||
![]() |
43e9529ce4 | ||
![]() |
0c258f0506 | ||
![]() |
912d5a3069 | ||
![]() |
1b6dd9241f | ||
![]() |
ecb4ee2e3e | ||
![]() |
7a1ae8691e | ||
![]() |
92972ac776 | ||
![]() |
0c469cc712 | ||
![]() |
3e4a14b299 | ||
![]() |
dff10e89fe | ||
![]() |
693adf8488 | ||
![]() |
adacfb1110 | ||
![]() |
177cc3d7f9 | ||
![]() |
0c582df962 | ||
![]() |
1e1fd97b38 | ||
![]() |
1e2f02613f | ||
![]() |
5a6a726014 | ||
![]() |
eace0af7a5 | ||
![]() |
036d0556a4 | ||
![]() |
e9fda40b2b | ||
![]() |
b9b2b559a1 | ||
![]() |
5fb3ea465f | ||
![]() |
ba04b753de | ||
![]() |
92ca447f33 | ||
![]() |
755a1331da | ||
![]() |
6db541c89b | ||
![]() |
67c52c3764 | ||
![]() |
131caa20eb | ||
![]() |
89a6ed2a5b | ||
![]() |
b597cd891b | ||
![]() |
fa31a6e441 | ||
![]() |
a3688fe642 | ||
![]() |
96e786d480 | ||
![]() |
3c09482a93 | ||
![]() |
a648da021f | ||
![]() |
d1d69bfaf4 | ||
![]() |
221e03488e | ||
![]() |
2ffb7cab2e | ||
![]() |
acffabf9de | ||
![]() |
0a464f9d28 | ||
![]() |
7add754fc3 | ||
![]() |
10ff950bb8 | ||
![]() |
6b9a4a8d6f | ||
![]() |
079ee658a5 | ||
![]() |
bd838a71d1 | ||
![]() |
5999af6c78 | ||
![]() |
af2eac52a8 | ||
![]() |
e4e040f14b | ||
![]() |
10789503c1 | ||
![]() |
fd15cdbf40 | ||
![]() |
9852376b38 | ||
![]() |
d327ec6ba4 | ||
![]() |
9e6d6b2532 | ||
![]() |
9edbff0ec0 | ||
![]() |
b82a3f3300 | ||
![]() |
d9fa6619e7 | ||
![]() |
f7d3c4b4ff | ||
![]() |
4ff92d739d | ||
![]() |
befd0f6ecd | ||
![]() |
b584185f0f | ||
![]() |
10b0924cfb | ||
![]() |
6736063f83 | ||
![]() |
93274a6d7b | ||
![]() |
16b763e086 | ||
![]() |
6974d4068b | ||
![]() |
0b65bb7e9a | ||
![]() |
1eb9ac8217 | ||
![]() |
7810dd1942 | ||
![]() |
eeb0456356 | ||
![]() |
c032cfd99e | ||
![]() |
4545249fa3 | ||
![]() |
380f719fd8 | ||
![]() |
fdf51a8855 | ||
![]() |
2d326f47ec | ||
![]() |
9c38cc42f6 | ||
![]() |
c27c89a680 | ||
![]() |
4e860b024b | ||
![]() |
0c896d9e59 | ||
![]() |
4f8bc641bd | ||
![]() |
e0a6119bb7 | ||
![]() |
7dc12dea1e | ||
![]() |
ab0a3690f3 | ||
![]() |
20c5578470 | ||
![]() |
bf921a41f9 | ||
![]() |
ab56ab9b27 | ||
![]() |
d3bc92c3f8 | ||
![]() |
2459eabb05 | ||
![]() |
f25809befb | ||
![]() |
443182c879 | ||
![]() |
452bd04272 | ||
![]() |
da9c961fca | ||
![]() |
feaf21373a | ||
![]() |
752b267399 | ||
![]() |
7966c925ea | ||
![]() |
832692c8af | ||
![]() |
14db7a09e3 | ||
![]() |
723a7c563f | ||
![]() |
21d6874e54 | ||
![]() |
354593a70d | ||
![]() |
38bf310eac | ||
![]() |
1146e61821 | ||
![]() |
8ee7504c45 | ||
![]() |
e824b55c20 | ||
![]() |
6d09cb6b6d | ||
![]() |
2d2b9a2ac9 | ||
![]() |
69661879eb | ||
![]() |
42e57547f7 | ||
![]() |
b88e24678b | ||
![]() |
de69d0031e | ||
![]() |
8998a79ff9 | ||
![]() |
e116f17c43 | ||
![]() |
efa1781eb6 | ||
![]() |
03e86ed147 | ||
![]() |
c754c860fd | ||
![]() |
391d00bcb9 | ||
![]() |
d7297b567d | ||
![]() |
e9cebedb4a | ||
![]() |
2edbf64e69 | ||
![]() |
1efcac0946 | ||
![]() |
415550f16d | ||
![]() |
aa554a9e77 | ||
![]() |
2876c7ff97 | ||
![]() |
651f56370a | ||
![]() |
950adeebbf | ||
![]() |
4e33a52290 | ||
![]() |
f2cc7c2873 | ||
![]() |
9c01ca1080 | ||
![]() |
09103dc981 | ||
![]() |
f096910abc | ||
![]() |
242165485d | ||
![]() |
e6904ca884 | ||
![]() |
5a792cc821 | ||
![]() |
0633eaf68c | ||
![]() |
40afa3695a | ||
![]() |
14889e7d85 | ||
![]() |
3bb103c6b6 | ||
![]() |
5e680551b9 | ||
![]() |
cefdd86b7f | ||
![]() |
b8e26a2112 | ||
![]() |
58a149990d | ||
![]() |
c23b2479f7 | ||
![]() |
a97b761eda | ||
![]() |
1adf8ff6b6 | ||
![]() |
b5a5363a6a | ||
![]() |
f91bdc2785 | ||
![]() |
db34f2f7fd | ||
![]() |
5de8ea162d | ||
![]() |
6e2763b72c | ||
![]() |
966d99217a | ||
![]() |
5d140145c1 | ||
![]() |
fcf3b0b672 | ||
![]() |
e8bbad6772 | ||
![]() |
5f2b75997f | ||
![]() |
426ce77f1c | ||
![]() |
83c7434eb5 | ||
![]() |
99854e90be | ||
![]() |
424b11cf50 | ||
![]() |
da87b1256c | ||
![]() |
a3971543b5 | ||
![]() |
a384798779 | ||
![]() |
d31eddf32f | ||
![]() |
1068c4ad23 | ||
![]() |
cbc979263e | ||
![]() |
765181bbc0 | ||
![]() |
d82dec9773 | ||
![]() |
024177515d | ||
![]() |
fb3b36a569 | ||
![]() |
614743c8f4 | ||
![]() |
47f5e4134e | ||
![]() |
efa7b3ba54 | ||
![]() |
1e9d67ec39 | ||
![]() |
80d0ddca9a | ||
![]() |
976d347623 | ||
![]() |
df0a059a02 | ||
![]() |
cc697486fc | ||
![]() |
2227a6f5f3 | ||
![]() |
a9320f06e8 | ||
![]() |
39b7ab66d4 | ||
![]() |
bc9ee1d611 | ||
![]() |
56ce747ffc | ||
![]() |
a2f3b2199a | ||
![]() |
88b8d34aa6 | ||
![]() |
21fd08e0fb | ||
![]() |
37a4f4a39f | ||
![]() |
9d2c30298e | ||
![]() |
6f5d60fb62 | ||
![]() |
41ffa0c015 | ||
![]() |
b3e51cc849 | ||
![]() |
e01e4e6530 | ||
![]() |
6ed072f67b | ||
![]() |
8bc64be77b | ||
![]() |
83e2cabbcc | ||
![]() |
7de7619fd1 | ||
![]() |
afae5fd972 | ||
![]() |
70cd313082 | ||
![]() |
e799f35dd2 | ||
![]() |
1db255fd3e | ||
![]() |
909e4820d6 | ||
![]() |
4727671c79 | ||
![]() |
f2f255e6e6 | ||
![]() |
b5d75e2016 | ||
![]() |
d3c35ec9c5 | ||
![]() |
d476656789 | ||
![]() |
8d0ff974e1 | ||
![]() |
33ded2a174 | ||
![]() |
277af33ab0 | ||
![]() |
2e4c005ad9 | ||
![]() |
739bed737e | ||
![]() |
a1a7e6ac06 | ||
![]() |
c3348bd068 | ||
![]() |
cc61729f01 | ||
![]() |
b457bfbd4e | ||
![]() |
1877834fd1 | ||
![]() |
afdfd1863f | ||
![]() |
f6aba0f9ec | ||
![]() |
66640ebfeb | ||
![]() |
9057e4b7d0 | ||
![]() |
0deb8f4090 | ||
![]() |
1633af7af6 | ||
![]() |
99da181cfc | ||
![]() |
8a9b0347bb | ||
![]() |
fe4b307fe6 | ||
![]() |
948ff5530c | ||
![]() |
2ff1a81f19 | ||
![]() |
d90527a095 | ||
![]() |
f0874ff3fd | ||
![]() |
c8538cc62f | ||
![]() |
bbe820d797 | ||
![]() |
b5751a3fa8 | ||
![]() |
02bc84062e | ||
![]() |
dd8a4c0c56 | ||
![]() |
4274fceafe | ||
![]() |
da06440fdc | ||
![]() |
c1c1d7fabb | ||
![]() |
8b39a1da00 | ||
![]() |
86e50f97ba | ||
![]() |
0d7ccc2b26 | ||
![]() |
5aa06ed3be | ||
![]() |
d696ce4e41 | ||
![]() |
2782dae518 | ||
![]() |
0f9c2f0a38 | ||
![]() |
296a5e786e | ||
![]() |
db0a55cd65 | ||
![]() |
32e79ce7b3 | ||
![]() |
a898610f13 | ||
![]() |
dc7df5bcfa | ||
![]() |
dcefd53bfe | ||
![]() |
cfa04fadd1 | ||
![]() |
4a6c337960 | ||
![]() |
3cf85bb837 | ||
![]() |
dc2c92e721 | ||
![]() |
b7f1a1ad4b | ||
![]() |
1967c1e237 | ||
![]() |
6e638cd673 | ||
![]() |
6fcc1d7685 | ||
![]() |
30ba3aeea7 | ||
![]() |
6172d80776 | ||
![]() |
811494e9ed | ||
![]() |
137b2ffdd0 | ||
![]() |
05e69da5d8 | ||
![]() |
b0f10b1851 | ||
![]() |
b1212fc98b | ||
![]() |
ea99567805 | ||
![]() |
443c842723 | ||
![]() |
9614da6238 | ||
![]() |
0164171cad | ||
![]() |
8e78160bb9 | ||
![]() |
7d118b5d42 | ||
![]() |
676ad2d34f | ||
![]() |
b3a82b1b20 | ||
![]() |
25848b78f9 | ||
![]() |
f94176a910 | ||
![]() |
ae96508e15 | ||
![]() |
95ebf815eb | ||
![]() |
b713fb5650 | ||
![]() |
6159c83fd2 | ||
![]() |
f1af17bf4d | ||
![]() |
a87c1c1210 | ||
![]() |
e63d165b65 | ||
![]() |
9411770253 | ||
![]() |
dc80ac1c88 | ||
![]() |
bb055628cc | ||
![]() |
390bcdb8c6 | ||
![]() |
d95bcb46ad | ||
![]() |
7b954e21e7 | ||
![]() |
a6eea4d096 | ||
![]() |
2c189d5c78 | ||
![]() |
85a80fd032 | ||
![]() |
0309b47515 | ||
![]() |
95d8f60389 | ||
![]() |
1ec7122381 | ||
![]() |
061b229e12 | ||
![]() |
3617433858 | ||
![]() |
d6d525cc1b | ||
![]() |
e752290458 | ||
![]() |
d77e25425e | ||
![]() |
028c0249a3 | ||
![]() |
a3ca5307a5 | ||
![]() |
6796462b13 | ||
![]() |
d08475d5af | ||
![]() |
d310c77fc8 | ||
![]() |
75d8ca1306 | ||
![]() |
894eea739e | ||
![]() |
1156290377 | ||
![]() |
c271f0c224 | ||
![]() |
a7f14dc103 | ||
![]() |
f05d5bdb9e | ||
![]() |
e99c400f59 | ||
![]() |
e38166837d | ||
![]() |
d43a08eb71 | ||
![]() |
293e713af6 | ||
![]() |
03866b4c31 | ||
![]() |
4f2c08525f | ||
![]() |
2c12f53937 | ||
![]() |
c88e5f9be2 | ||
![]() |
0f51a9794e | ||
![]() |
edd1f49e57 | ||
![]() |
4df0cf2d07 | ||
![]() |
87ba99755b | ||
![]() |
c03f860f8e | ||
![]() |
f2e0e3f345 | ||
![]() |
fee652dfd7 | ||
![]() |
839446a88d | ||
![]() |
028b8c8bcc | ||
![]() |
64b1d4ca3b | ||
![]() |
c6cbee6563 | ||
![]() |
a406f6e7cc | ||
![]() |
9869b92c2b | ||
![]() |
00549eed79 | ||
![]() |
0c4968dc30 | ||
![]() |
704335c898 | ||
![]() |
ec74feea5a | ||
![]() |
2f5cc3059a | ||
![]() |
4355485581 | ||
![]() |
342c3254cb | ||
![]() |
5fc82dfaa2 | ||
![]() |
6ab6507db9 | ||
![]() |
3c807ae86e | ||
![]() |
9bfacaa39a | ||
![]() |
a2882a4908 | ||
![]() |
1adc64a352 | ||
![]() |
c28863966b | ||
![]() |
14dc679332 | ||
![]() |
17085dd8a0 | ||
![]() |
82b8313da0 | ||
![]() |
4f7e764fa0 | ||
![]() |
d52da8bbea | ||
![]() |
cdddcad784 | ||
![]() |
38767cad0f | ||
![]() |
c3d7dda61f | ||
![]() |
c4e32ce159 | ||
![]() |
6355a07dc4 | ||
![]() |
0e3fb41e73 | ||
![]() |
fdac5af5ee | ||
![]() |
0e509ceafa | ||
![]() |
6b84534632 | ||
![]() |
fc255b558d | ||
![]() |
9e54e30011 | ||
![]() |
77312ce2e0 | ||
![]() |
9a6d29d6e7 | ||
![]() |
2cb7517f64 | ||
![]() |
3228882fc0 | ||
![]() |
6804e3dc73 | ||
![]() |
f9af61a5ca | ||
![]() |
a94b443f13 | ||
![]() |
fd06aa2135 | ||
![]() |
dd0f40559d | ||
![]() |
471a60dcb0 | ||
![]() |
46994c3355 | ||
![]() |
642811869c | ||
![]() |
3be4697487 | ||
![]() |
a3aca4acb5 | ||
![]() |
7587f858ae | ||
![]() |
854c214bc0 | ||
![]() |
5dfce4db34 | ||
![]() |
95467fa3c1 | ||
![]() |
4ec3453558 | ||
![]() |
536fda04f2 | ||
![]() |
2094204877 | ||
![]() |
ab375cca1a | ||
![]() |
479f706f8a | ||
![]() |
4342285507 | ||
![]() |
8bb656cb17 | ||
![]() |
a117e897ca | ||
![]() |
347ac70063 | ||
![]() |
50842ef815 | ||
![]() |
1970a64f6f | ||
![]() |
dd71a53f5e | ||
![]() |
3f1f835df3 | ||
![]() |
8440d9890c | ||
![]() |
87ca031335 | ||
![]() |
96b9e37461 | ||
![]() |
0d3a2fe844 | ||
![]() |
848781aef5 | ||
![]() |
28bf497a0b | ||
![]() |
8ede738396 | ||
![]() |
40c2b6a563 | ||
![]() |
3581cf7305 | ||
![]() |
c33775b944 | ||
![]() |
b0cd2522e0 | ||
![]() |
c3979f6e31 | ||
![]() |
103df4d9f3 | ||
![]() |
040e02cfc5 | ||
![]() |
f377b64065 | ||
![]() |
e5459b68ff | ||
![]() |
fc194021a4 | ||
![]() |
39f8ca3bf1 | ||
![]() |
7a807f7216 | ||
![]() |
bedfb51b1c | ||
![]() |
b2afb95c19 |
2567 changed files with 304561 additions and 77603 deletions
20
.dockerignore
Normal file
20
.dockerignore
Normal file
|
@ -0,0 +1,20 @@
|
|||
.vscode/
|
||||
cli/
|
||||
design/
|
||||
docker/
|
||||
docs/
|
||||
fastlane/
|
||||
machine-learning/
|
||||
misc/
|
||||
mobile/
|
||||
|
||||
server/node_modules
|
||||
server/coverage/
|
||||
server/.reverse-geocoding-dump/
|
||||
server/upload/
|
||||
server/dist/
|
||||
|
||||
web/node_modules/
|
||||
web/coverage/
|
||||
web/.svelte-kit
|
||||
web/build/
|
19
.editorconfig
Normal file
19
.editorconfig
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{ts,js}]
|
||||
quote_type = single
|
||||
|
||||
[*.{md,mdx}]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{yml,yaml}]
|
||||
quote_type = double
|
21
.gitattributes
vendored
Normal file
21
.gitattributes
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
mobile/openapi/**/*.md -diff -merge
|
||||
mobile/openapi/**/*.md linguist-generated=true
|
||||
mobile/openapi/**/*.dart -diff -merge
|
||||
mobile/openapi/**/*.dart linguist-generated=true
|
||||
mobile/openapi/.openapi-generator/FILES -diff -merge
|
||||
mobile/openapi/.openapi-generator/FILES linguist-generated=true
|
||||
|
||||
mobile/lib/**/*.g.dart -diff -merge
|
||||
mobile/lib/**/*.g.dart linguist-generated=true
|
||||
|
||||
cli/src/api/open-api/**/*.md -diff -merge
|
||||
cli/src/api/open-api/**/*.md linguist-generated=true
|
||||
cli/src/api/open-api/**/*.ts -diff -merge
|
||||
cli/src/api/open-api/**/*.ts linguist-generated=true
|
||||
|
||||
web/src/api/open-api/**/*.md -diff -merge
|
||||
web/src/api/open-api/**/*.md linguist-generated=true
|
||||
web/src/api/open-api/**/*.ts -diff -merge
|
||||
web/src/api/open-api/**/*.ts linguist-generated=true
|
||||
|
||||
*.sh text eol=lf
|
24
.github/DISCUSSION_TEMPLATE/feature-request.yaml
vendored
Normal file
24
.github/DISCUSSION_TEMPLATE/feature-request.yaml
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
title: "[Feature] <feature-name-goes-here>"
|
||||
labels: ["feature"]
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please use this form to request new feature for Immich
|
||||
- type: textarea
|
||||
id: feature
|
||||
attributes:
|
||||
label: The feature
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Platform
|
||||
options:
|
||||
- label: Server
|
||||
- label: Web
|
||||
- label: Mobile
|
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
|
@ -1,4 +1,5 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: alextran1502
|
||||
github: immich-app
|
||||
liberapay: alex.tran1502
|
||||
custom: https://www.buymeacoffee.com/altran1502
|
||||
|
|
46
.github/ISSUE_TEMPLATE/bug_report.md
vendored
46
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -1,46 +0,0 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: '[BUG] <title>'
|
||||
labels: bug, need triage
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Note: Please search to see if an issue already exists for the bug you encountered.
|
||||
-->
|
||||
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Task List**
|
||||
|
||||
*Please complete the task list below. We need this information to help us reproduce the bug or point out problems in your setup. You are not providing enough info may delay our effort to help you.*
|
||||
|
||||
- [ ] I have read thoroughly the README setup and installation instructions.
|
||||
- [ ] I have included my `docker-compose` file.
|
||||
- [ ] I have included my redacted `.env` file.
|
||||
- [ ] I have included information on my machine, and environment.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**System**
|
||||
- Phone OS [iOS, Android]: `<version>`
|
||||
- Server Version: `<version>`
|
||||
- Mobile App Version: `<version>`
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
100
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
100
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
|
@ -0,0 +1,100 @@
|
|||
name: Report an issue with Immich
|
||||
description: Report an issue with Immich
|
||||
labels: ["bug", "need triage"]
|
||||
title: "[BUG] <title>"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
This issue form is for reporting bugs only!
|
||||
|
||||
If you have a feature or enhancement request, please use the [feature request][fr] section of our [GitHub Discussions][fr].
|
||||
|
||||
[fr]: https://github.com/immich-app/immich/discussions/new?category=feature-request
|
||||
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: The bug
|
||||
description: >-
|
||||
Describe the issue you are experiencing here, to communicate to the
|
||||
maintainers. Tell us what you were trying to do and what happened.
|
||||
|
||||
Provide a clear and concise description of what the problem is.
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Environment
|
||||
|
||||
- type: input
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: The OS that Immich Server is running on
|
||||
placeholder: Ubuntu 22.10, Debian, Arch...etc
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Version of Immich Server
|
||||
placeholder: v1.0.0
|
||||
|
||||
- type: input
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Version of Immich Mobile App
|
||||
placeholder: v1.0.0
|
||||
|
||||
- type: checkboxes
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Platform with the issue
|
||||
options:
|
||||
- label: Server
|
||||
- label: Web
|
||||
- label: Mobile
|
||||
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Your docker-compose.yml content
|
||||
render: YAML
|
||||
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Your .env content
|
||||
description: Please provide the redacted .env content of your setup
|
||||
render: Shell
|
||||
|
||||
- type: textarea
|
||||
id: repro
|
||||
attributes:
|
||||
label: Reproduction steps
|
||||
description: "How do you trigger this bug? Please walk us through it step by step."
|
||||
value: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
...
|
||||
render: bash
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: >
|
||||
If you have any additional information for us, use the field below.
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Thank you for submitting the form
|
10
.github/ISSUE_TEMPLATE/config.yml
vendored
10
.github/ISSUE_TEMPLATE/config.yml
vendored
|
@ -1 +1,11 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: I have a question or need support
|
||||
url: https://discord.gg/D8JsnBEuKb
|
||||
about: We use GitHub for tracking bugs, please check out our Discord channel for freaky fast support.
|
||||
- name: Feature Request
|
||||
url: https://github.com/immich-app/immich/discussions/new?category=feature-request
|
||||
about: Please use our GitHub Discussion for making feature requests.
|
||||
- name: I'm unsure where to go
|
||||
url: https://discord.gg/D8JsnBEuKb
|
||||
about: If you are unsure where to go, then joining our Discord is recommended; Just ask!
|
||||
|
|
32
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
32
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
|
@ -1,32 +0,0 @@
|
|||
name: Feature Request
|
||||
description: Request a feature that you would like for the app
|
||||
title: "[Feature]: "
|
||||
labels: ["feature", "need triage"]
|
||||
assignees:
|
||||
- ""
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this feature request!
|
||||
|
||||
- type: textarea
|
||||
id: feature-detail
|
||||
attributes:
|
||||
label: Feature detail
|
||||
placeholder: Describe the feature you would like to see for the app
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: platform
|
||||
attributes:
|
||||
label: Platform
|
||||
description: Choose the platform for the feature
|
||||
options:
|
||||
- Web
|
||||
- Mobile App
|
||||
- Server
|
||||
validations:
|
||||
required: true
|
2
.github/PULL_REQUEST_TEMPLATE/config.yml
vendored
Normal file
2
.github/PULL_REQUEST_TEMPLATE/config.yml
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
blank_issues_enabled: false
|
||||
blank_pull_request_template_enabled: false
|
22
.github/PULL_REQUEST_TEMPLATE/pull_request_template.md
vendored
Normal file
22
.github/PULL_REQUEST_TEMPLATE/pull_request_template.md
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
## Description
|
||||
<!--- Describe your changes in detail -->
|
||||
<!--- Why is this change required? What problem does it solve? -->
|
||||
<!--- If it fixes an open issue, please link to the issue here. -->
|
||||
|
||||
Fixes # (issue)
|
||||
|
||||
|
||||
## How Has This Been Tested?
|
||||
|
||||
<!-- Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration -->
|
||||
|
||||
- [ ] Test A
|
||||
- [ ] Test B
|
||||
|
||||
## Screenshots (if appropriate):
|
||||
|
||||
|
||||
## Checklist:
|
||||
|
||||
- [ ] I have performed a self-review of my own code
|
||||
- [ ] I have made corresponding changes to the documentation if applicable
|
75
.github/workflows/build-mobile.yml
vendored
Normal file
75
.github/workflows/build-mobile.yml
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
name: Build Mobile
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
inputs:
|
||||
ref:
|
||||
required: false
|
||||
type: string
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-sign-android:
|
||||
name: Build and sign Android
|
||||
# Skip when PR from a fork
|
||||
if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' }}
|
||||
runs-on: macos-13
|
||||
|
||||
steps:
|
||||
- name: Determine ref
|
||||
id: get-ref
|
||||
run: |
|
||||
input_ref="${{ inputs.ref }}"
|
||||
github_ref="${{ github.sha }}"
|
||||
ref="${input_ref:-$github_ref}"
|
||||
echo "ref=$ref" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ steps.get-ref.outputs.ref }}
|
||||
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: "zulu"
|
||||
java-version: "12.x"
|
||||
cache: "gradle"
|
||||
|
||||
- name: Setup Flutter SDK
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: "3.13.6"
|
||||
cache: true
|
||||
|
||||
- name: Create the Keystore
|
||||
env:
|
||||
KEY_JKS: ${{ secrets.KEY_JKS }}
|
||||
working-directory: ./mobile
|
||||
run: echo $KEY_JKS | base64 -d > android/key.jks
|
||||
|
||||
- name: Get Packages
|
||||
working-directory: ./mobile
|
||||
run: flutter pub get
|
||||
|
||||
- name: Build Android App Bundle
|
||||
working-directory: ./mobile
|
||||
env:
|
||||
ALIAS: ${{ secrets.ALIAS }}
|
||||
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
||||
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
|
||||
run: |
|
||||
flutter build apk --release
|
||||
flutter build apk --release --split-per-abi --target-platform android-arm,android-arm64,android-x64
|
||||
|
||||
- name: Publish Android Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: release-apk-signed
|
||||
path: mobile/build/app/outputs/flutter-apk/*.apk
|
120
.github/workflows/build_push_docker_latest.yml
vendored
120
.github/workflows/build_push_docker_latest.yml
vendored
|
@ -1,120 +0,0 @@
|
|||
name: Build and Push Docker Image - Latest
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
# This image include both the server and microservices - the two containers can be slitted into separated
|
||||
# service with its coressponding entry file.
|
||||
build_and_push_server_monorepo_latest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push Immich Mono Repo
|
||||
uses: docker/build-push-action@v3.1.1
|
||||
with:
|
||||
context: ./server
|
||||
file: ./server/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
altran1502/immich-server:latest
|
||||
|
||||
build_and_push_machine_learning_latest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and Push Machine Learning
|
||||
uses: docker/build-push-action@v3.1.1
|
||||
with:
|
||||
context: ./machine-learning
|
||||
file: ./machine-learning/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
altran1502/immich-machine-learning:latest
|
||||
|
||||
build_and_push_web_latest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and Push Web
|
||||
uses: docker/build-push-action@v3.1.1
|
||||
with:
|
||||
context: ./web
|
||||
file: ./web/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
target: prod
|
||||
push: true
|
||||
tags: |
|
||||
altran1502/immich-web:latest
|
||||
|
||||
build_and_push_nginx_latest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and Push Proxy
|
||||
uses: docker/build-push-action@v3.1.1
|
||||
with:
|
||||
context: ./nginx
|
||||
file: ./nginx/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
altran1502/immich-proxy:latest
|
126
.github/workflows/build_push_docker_staging.yml
vendored
126
.github/workflows/build_push_docker_staging.yml
vendored
|
@ -1,126 +0,0 @@
|
|||
name: Build and Push Docker Image - Staging
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
# This image include both the server and microservices - the two containers can be slitted into separated
|
||||
# service with its coressponding entry file.
|
||||
build_and_push_server_monorepo_staging:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
- name: Login to Docker Hub
|
||||
if: ${{ github.repository == 'immich-app/immich' }}
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push Immich Mono Repo
|
||||
uses: docker/build-push-action@v3.1.1
|
||||
with:
|
||||
context: ./server
|
||||
file: ./server/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name == 'pull_request' && github.repository == 'immich-app/immich' }}
|
||||
tags: |
|
||||
altran1502/immich-server:staging
|
||||
|
||||
build_and_push_machine_learning_staging:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
- name: Login to Docker Hub
|
||||
if: ${{ github.repository == 'immich-app/immich' }}
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and Push Machine Learning
|
||||
uses: docker/build-push-action@v3.1.1
|
||||
with:
|
||||
context: ./machine-learning
|
||||
file: ./machine-learning/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name == 'pull_request' && github.repository == 'immich-app/immich' }}
|
||||
tags: |
|
||||
altran1502/immich-machine-learning:staging
|
||||
|
||||
build_and_push_web_staging:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
- name: Login to Docker Hub
|
||||
if: ${{ github.repository == 'immich-app/immich' }}
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and Push Web
|
||||
uses: docker/build-push-action@v3.1.1
|
||||
with:
|
||||
context: ./web
|
||||
file: ./web/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
target: prod
|
||||
push: ${{ github.event_name == 'pull_request' && github.repository == 'immich-app/immich' }}
|
||||
tags: |
|
||||
altran1502/immich-web:staging
|
||||
|
||||
build_and_push_nginx_staging:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
- name: Login to Docker Hub
|
||||
if: ${{ github.repository == 'immich-app/immich' }}
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and Push Proxy
|
||||
uses: docker/build-push-action@v3.1.1
|
||||
with:
|
||||
context: ./nginx
|
||||
file: ./nginx/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name == 'pull_request' && github.repository == 'immich-app/immich' }}
|
||||
tags: |
|
||||
altran1502/immich-proxy:staging
|
158
.github/workflows/build_push_server_release.yml
vendored
158
.github/workflows/build_push_server_release.yml
vendored
|
@ -1,158 +0,0 @@
|
|||
name: Build and push Docker image - Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build_and_push_server_monorepo_release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: "main"
|
||||
fetch-depth: 0
|
||||
|
||||
- name: "Get Previous tag"
|
||||
id: previoustag
|
||||
uses: "WyriHaximus/github-action-get-previous-tag@v1"
|
||||
with:
|
||||
fallback: latest
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push immich-server release
|
||||
uses: docker/build-push-action@v3.1.1
|
||||
with:
|
||||
context: ./server
|
||||
file: ./server/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: |
|
||||
altran1502/immich-server:${{ steps.previoustag.outputs.tag }}
|
||||
altran1502/immich-server:release
|
||||
|
||||
build_and_push_machine_learning_release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: "Get Previous tag"
|
||||
id: previoustag
|
||||
uses: "WyriHaximus/github-action-get-previous-tag@v1"
|
||||
with:
|
||||
fallback: latest
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and Push Machine Learning
|
||||
uses: docker/build-push-action@v3.1.1
|
||||
with:
|
||||
context: ./machine-learning
|
||||
file: ./machine-learning/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
altran1502/immich-machine-learning:${{ steps.previoustag.outputs.tag }}
|
||||
altran1502/immich-machine-learning:release
|
||||
|
||||
build_and_push_web_release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: "main"
|
||||
fetch-depth: 0
|
||||
|
||||
- name: "Get Previous tag"
|
||||
id: previoustag
|
||||
uses: "WyriHaximus/github-action-get-previous-tag@v1"
|
||||
with:
|
||||
fallback: latest
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push immich-web release
|
||||
uses: docker/build-push-action@v3.1.1
|
||||
with:
|
||||
context: ./web
|
||||
file: ./web/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
target: prod
|
||||
tags: |
|
||||
altran1502/immich-web:${{ steps.previoustag.outputs.tag }}
|
||||
altran1502/immich-web:release
|
||||
|
||||
build_and_push_nginx_release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: "main"
|
||||
fetch-depth: 0
|
||||
|
||||
- name: "Get Previous tag"
|
||||
id: previoustag
|
||||
uses: "WyriHaximus/github-action-get-previous-tag@v1"
|
||||
with:
|
||||
fallback: latest
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push immich-proxy release
|
||||
uses: docker/build-push-action@v3.1.1
|
||||
with:
|
||||
context: ./nginx
|
||||
file: ./nginx/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: |
|
||||
altran1502/immich-proxy:release
|
||||
altran1502/immich-proxy:${{ steps.previoustag.outputs.tag }}
|
38
.github/workflows/cache-cleanup.yml
vendored
Normal file
38
.github/workflows/cache-cleanup.yml
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
name: Cache Cleanup
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
name: Cleanup
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Cleanup
|
||||
run: |
|
||||
gh extension install actions/gh-actions-cache
|
||||
|
||||
REPO=${{ github.repository }}
|
||||
BRANCH=${{ github.ref }}
|
||||
|
||||
echo "Fetching list of cache keys"
|
||||
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 )
|
||||
|
||||
## Setting this to not fail the workflow while deleting cache keys.
|
||||
set +e
|
||||
echo "Deleting caches..."
|
||||
for cacheKey in $cacheKeysForPR
|
||||
do
|
||||
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
|
||||
done
|
||||
echo "Done"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
23
.github/workflows/cli-release.yml
vendored
Normal file
23
.github/workflows/cli-release.yml
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
name: CLI Release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Publish
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./cli
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# Setup .npmrc file to publish to npm
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20.x"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
- run: npm ci
|
||||
- run: npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
78
.github/workflows/codeql-analysis.yml
vendored
Normal file
78
.github/workflows/codeql-analysis.yml
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "main" ]
|
||||
schedule:
|
||||
- cron: '20 13 * * 1'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript', 'python' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
26
.github/workflows/dispatch_sdk_update.yml
vendored
Normal file
26
.github/workflows/dispatch_sdk_update.yml
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
name: Update Immich SDK
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: ["main"]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
update-sdk-repos:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
steps:
|
||||
- uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN }}
|
||||
script: |
|
||||
await github.rest.actions.createWorkflowDispatch({
|
||||
owner: 'immich-app',
|
||||
repo: 'immich-sdk-typescript-axios',
|
||||
workflow_id: 'build.yml',
|
||||
ref: 'main'
|
||||
})
|
73
.github/workflows/docker-cleanup.yml
vendored
Normal file
73
.github/workflows/docker-cleanup.yml
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
# This workflow runs on certain conditions to check for and potentially
|
||||
# delete container images from the GHCR which no longer have an associated
|
||||
# code branch.
|
||||
# Requires a PAT with the correct scope set in the secrets.
|
||||
#
|
||||
# This workflow will not trigger runs on forked repos.
|
||||
|
||||
name: Docker Cleanup
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- "closed"
|
||||
push:
|
||||
paths:
|
||||
- ".github/workflows/docker-cleanup.yml"
|
||||
|
||||
concurrency:
|
||||
group: registry-tags-cleanup
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
cleanup-images:
|
||||
name: Cleanup Stale Images Tags for ${{ matrix.primary-name }}
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- primary-name: "immich-server"
|
||||
- primary-name: "immich-machine-learning"
|
||||
env:
|
||||
# Requires a personal access token with the OAuth scope delete:packages
|
||||
TOKEN: ${{ secrets.PACKAGE_DELETE_TOKEN }}
|
||||
steps:
|
||||
- name: Clean temporary images
|
||||
if: "${{ env.TOKEN != '' }}"
|
||||
uses: stumpylog/image-cleaner-action/ephemeral@v0.4.0
|
||||
with:
|
||||
token: "${{ env.TOKEN }}"
|
||||
owner: "immich-app"
|
||||
is_org: "true"
|
||||
do_delete: "true"
|
||||
package_name: "${{ matrix.primary-name }}"
|
||||
scheme: "pull_request"
|
||||
repo_name: "immich"
|
||||
match_regex: '^pr-(\d+)$|^(\d+)$'
|
||||
|
||||
cleanup-untagged-images:
|
||||
name: Cleanup Untagged Images Tags for ${{ matrix.primary-name }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- cleanup-images
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- primary-name: "immich-server"
|
||||
- primary-name: "immich-machine-learning"
|
||||
- primary-name: "immich-build-cache"
|
||||
env:
|
||||
# Requires a personal access token with the OAuth scope delete:packages
|
||||
TOKEN: ${{ secrets.PACKAGE_DELETE_TOKEN }}
|
||||
steps:
|
||||
- name: Clean untagged images
|
||||
if: "${{ env.TOKEN != '' }}"
|
||||
uses: stumpylog/image-cleaner-action/untagged@v0.4.0
|
||||
with:
|
||||
token: "${{ env.TOKEN }}"
|
||||
owner: "immich-app"
|
||||
do_delete: "true"
|
||||
is_org: "true"
|
||||
package_name: "${{ matrix.primary-name }}"
|
110
.github/workflows/docker.yml
vendored
Normal file
110
.github/workflows/docker.yml
vendored
Normal file
|
@ -0,0 +1,110 @@
|
|||
name: Docker
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
build_and_push:
|
||||
name: Build and Push
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
# Prevent a failure in one image from stopping the other builds
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- context: "machine-learning"
|
||||
file: "machine-learning/Dockerfile"
|
||||
image: "immich-machine-learning"
|
||||
platforms: "linux/amd64,linux/arm64"
|
||||
- context: "."
|
||||
file: "server/Dockerfile"
|
||||
image: "immich-server"
|
||||
platforms: "linux/arm64,linux/amd64"
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.0.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.0.0
|
||||
# Workaround to fix error:
|
||||
# failed to push: failed to copy: io: read/write on closed pipe
|
||||
# See https://github.com/docker/build-push-action/issues/761
|
||||
with:
|
||||
driver-opts: |
|
||||
image=moby/buildkit:v0.10.6
|
||||
|
||||
- name: Login to Docker Hub
|
||||
# Only push to Docker Hub when making a release
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
# Skip when PR from a fork
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Generate docker image tags
|
||||
id: metadata
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
flavor: |
|
||||
# Disable latest tag
|
||||
latest=false
|
||||
images: |
|
||||
name=ghcr.io/${{ github.repository_owner }}/${{matrix.image}}
|
||||
name=altran1502/${{matrix.image}},enable=${{ github.event_name == 'release' }}
|
||||
tags: |
|
||||
# Tag with branch name
|
||||
type=ref,event=branch
|
||||
# Tag with pr-number
|
||||
type=ref,event=pr
|
||||
# Tag with git tag on release
|
||||
type=ref,event=tag
|
||||
type=raw,value=release,enable=${{ github.event_name == 'release' }}
|
||||
|
||||
- name: Determine build cache output
|
||||
id: cache-target
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
# Essentially just ignore the cache output (PR can't write to registry cache)
|
||||
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "cache-to=type=registry,mode=max,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{ matrix.image }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Build and push image
|
||||
uses: docker/build-push-action@v5.1.0
|
||||
with:
|
||||
context: ${{ matrix.context }}
|
||||
file: ${{ matrix.file }}
|
||||
platforms: ${{ matrix.platforms }}
|
||||
# Skip pushing when PR from a fork
|
||||
push: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{matrix.image}}
|
||||
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
||||
tags: ${{ steps.metadata.outputs.tags }}
|
||||
labels: ${{ steps.metadata.outputs.labels }}
|
19
.github/workflows/github-repo-stats.yml
vendored
19
.github/workflows/github-repo-stats.yml
vendored
|
@ -1,19 +0,0 @@
|
|||
name: github-repo-stats
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run this once per day, towards the end of the day for keeping the most
|
||||
# recent data point most meaningful (hours are interpreted in UTC).
|
||||
- cron: "0 23 * * *"
|
||||
workflow_dispatch: # Allow for running this manually.
|
||||
|
||||
jobs:
|
||||
j1:
|
||||
name: github-repo-stats
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: run-ghrs
|
||||
# Use latest release.
|
||||
uses: jgehrcke/github-repo-stats@RELEASE
|
||||
with:
|
||||
ghtoken: ${{ secrets.GHRS_GITHUB_API_TOKEN }}
|
87
.github/workflows/prepare-release.yml
vendored
Normal file
87
.github/workflows/prepare-release.yml
vendored
Normal file
|
@ -0,0 +1,87 @@
|
|||
name: Prepare new release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
serverBump:
|
||||
description: "Bump server version"
|
||||
required: true
|
||||
default: "false"
|
||||
type: choice
|
||||
options:
|
||||
- "false"
|
||||
- minor
|
||||
- patch
|
||||
mobileBump:
|
||||
description: "Bump mobile build number"
|
||||
required: false
|
||||
type: boolean
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-root
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
bump_version:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
ref: ${{ steps.push-tag.outputs.commit_long_sha }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.ORG_RELEASE_TOKEN }}
|
||||
|
||||
- name: Install Poetry
|
||||
run: pipx install poetry
|
||||
|
||||
- name: Bump version
|
||||
run: misc/release/pump-version.sh -s "${{ inputs.serverBump }}" -m "${{ inputs.mobileBump }}"
|
||||
|
||||
- name: Commit and tag
|
||||
id: push-tag
|
||||
uses: EndBug/add-and-commit@v9
|
||||
with:
|
||||
author_name: Alex The Bot
|
||||
author_email: alex.tran1502@gmail.com
|
||||
default_author: user_info
|
||||
message: "Version ${{ env.IMMICH_VERSION }}"
|
||||
tag: ${{ env.IMMICH_VERSION }}
|
||||
push: true
|
||||
|
||||
build_mobile:
|
||||
uses: ./.github/workflows/build-mobile.yml
|
||||
needs: bump_version
|
||||
secrets: inherit
|
||||
with:
|
||||
ref: ${{ needs.bump_version.outputs.ref }}
|
||||
|
||||
prepare_release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build_mobile
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.ORG_RELEASE_TOKEN }}
|
||||
|
||||
- name: Download APK
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: release-apk-signed
|
||||
|
||||
- name: Create draft release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
tag_name: ${{ env.IMMICH_VERSION }}
|
||||
generate_release_notes: true
|
||||
body_path: misc/release/notes.tmpl
|
||||
files: |
|
||||
docker/docker-compose.yml
|
||||
docker/example.env
|
||||
docker/hwaccel.yml
|
||||
*.apk
|
39
.github/workflows/static_analysis.yml
vendored
Normal file
39
.github/workflows/static_analysis.yml
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
name: Static Code Analysis
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
mobile-dart-analyze:
|
||||
name: Run Dart Code Analysis
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Flutter SDK
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: "3.13.6"
|
||||
|
||||
- name: Install dependencies
|
||||
run: dart pub get
|
||||
working-directory: ./mobile
|
||||
|
||||
- name: Run dart analyze
|
||||
run: dart analyze --fatal-infos
|
||||
working-directory: ./mobile
|
||||
|
||||
# Enable after riverpod generator migration is completed
|
||||
# - name: Run dart custom lint
|
||||
# run: dart run custom_lint
|
||||
# working-directory: ./mobile
|
335
.github/workflows/test.yml
vendored
335
.github/workflows/test.yml
vendored
|
@ -5,37 +5,348 @@ on:
|
|||
push:
|
||||
branches: [main]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
e2e-tests:
|
||||
name: Run end-to-end test suites
|
||||
|
||||
name: Server (e2e)
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: "recursive"
|
||||
|
||||
- name: Run Immich Server 2E2 Test
|
||||
run: docker-compose -f ./docker/docker-compose.test.yml --env-file ./docker/.env.test up --abort-on-container-exit --exit-code-from immich-server-test
|
||||
- name: Run e2e tests
|
||||
run: docker compose -f ./docker/docker-compose.test.yml up --renew-anon-volumes --abort-on-container-exit --exit-code-from immich-server --remove-orphans --build
|
||||
|
||||
doc-tests:
|
||||
name: Docs
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./docs
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run npm install
|
||||
run: npm ci
|
||||
|
||||
- name: Run formatter
|
||||
run: npm run format
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run tsc
|
||||
run: npm run check
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run build
|
||||
run: npm run build
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
server-unit-tests:
|
||||
name: Run server unit test suites and checks
|
||||
name: Server
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./server
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run tests
|
||||
run: cd server && npm ci && npm run check:all
|
||||
- name: Run npm install
|
||||
run: npm ci
|
||||
|
||||
- name: Run linter
|
||||
run: npm run lint
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run formatter
|
||||
run: npm run format
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run tsc
|
||||
run: npm run check
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run unit tests & coverage
|
||||
run: npm run test:cov
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
cli-unit-tests:
|
||||
name: CLI
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./cli
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run npm install
|
||||
run: npm ci
|
||||
|
||||
- name: Run linter
|
||||
run: npm run lint
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run formatter
|
||||
run: npm run format
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run tsc
|
||||
run: npm run check
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run unit tests & coverage
|
||||
run: npm run test:cov
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
web-unit-tests:
|
||||
name: Run web unit test suites and checks
|
||||
name: Web
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./web
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run npm install
|
||||
run: npm ci
|
||||
|
||||
- name: Run linter
|
||||
run: npm run lint
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run formatter
|
||||
run: npm run format
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run svelte checks
|
||||
run: npm run check:svelte
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run tsc
|
||||
run: npm run check:typescript
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
# - name: Run unit tests & coverage
|
||||
# run: npm run test:cov
|
||||
# if: ${{ !cancelled() }}
|
||||
|
||||
mobile-unit-tests:
|
||||
name: Mobile
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Flutter SDK
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: "3.13.6"
|
||||
- name: Run tests
|
||||
run: cd web && npm ci && npm run check:all
|
||||
working-directory: ./mobile
|
||||
run: flutter test -j 1
|
||||
|
||||
ml-unit-tests:
|
||||
name: Machine Learning
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./machine-learning
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install poetry
|
||||
run: pipx install poetry
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.11
|
||||
cache: "poetry"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
poetry install --with dev
|
||||
- name: Lint with ruff
|
||||
run: |
|
||||
poetry run ruff check --format=github app export
|
||||
- name: Check black formatting
|
||||
run: |
|
||||
poetry run black --check app export
|
||||
- name: Run mypy type checking
|
||||
run: |
|
||||
poetry run mypy --install-types --non-interactive --strict app/ export/
|
||||
- name: Run tests and coverage
|
||||
run: |
|
||||
poetry run pytest --cov app
|
||||
|
||||
generated-api-up-to-date:
|
||||
name: OpenAPI Clients
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run API generation
|
||||
run: npm --prefix server run api:generate
|
||||
- name: Find file changes
|
||||
uses: tj-actions/verify-changed-files@v13.1
|
||||
id: verify-changed-files
|
||||
with:
|
||||
files: |
|
||||
mobile/openapi
|
||||
web/src/api/open-api
|
||||
- name: Verify files have not changed
|
||||
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
||||
run: |
|
||||
echo "ERROR: Generated files not up to date!"
|
||||
echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}"
|
||||
exit 1
|
||||
|
||||
generated-typeorm-migrations-up-to-date:
|
||||
name: TypeORM Checks
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres@sha256:6dfee32131933ab4ca25a00360c3f427fdc134de56f9a90c6c9a4956b48aea85
|
||||
env:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: immich
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./server
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install server dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build the app
|
||||
run: npm run build
|
||||
|
||||
- name: Run existing migrations
|
||||
run: npm run typeorm:migrations:run
|
||||
|
||||
- name: Generate new migrations
|
||||
continue-on-error: true
|
||||
run: npm run typeorm:migrations:generate ./src/infra/migrations/TestMigration
|
||||
|
||||
- name: Find file changes
|
||||
uses: tj-actions/verify-changed-files@v13.1
|
||||
id: verify-changed-files
|
||||
with:
|
||||
files: |
|
||||
server/src/infra/migrations/
|
||||
- name: Verify migration files have not changed
|
||||
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
||||
run: |
|
||||
echo "ERROR: Generated migration files not up to date!"
|
||||
echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}"
|
||||
exit 1
|
||||
|
||||
- name: Run SQL generation
|
||||
run: npm run sql:generate
|
||||
|
||||
- name: Find file changes
|
||||
uses: tj-actions/verify-changed-files@v13.1
|
||||
id: verify-changed-sql-files
|
||||
with:
|
||||
files: |
|
||||
server/src/infra/sql
|
||||
|
||||
- name: Verify SQL files have not changed
|
||||
if: steps.verify-changed-sql-files.outputs.files_changed == 'true'
|
||||
run: |
|
||||
echo "ERROR: Generated SQL files not up to date!"
|
||||
echo "Changed files: ${{ steps.verify-changed-sql-files.outputs.changed_files }}"
|
||||
exit 1
|
||||
|
||||
# mobile-integration-tests:
|
||||
# name: Run mobile end-to-end integration tests
|
||||
# runs-on: macos-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v4
|
||||
# - uses: actions/setup-java@v3
|
||||
# with:
|
||||
# distribution: 'zulu'
|
||||
# java-version: '12.x'
|
||||
# cache: 'gradle'
|
||||
# - name: Cache android SDK
|
||||
# uses: actions/cache@v3
|
||||
# id: android-sdk
|
||||
# with:
|
||||
# key: android-sdk
|
||||
# path: |
|
||||
# /usr/local/lib/android/
|
||||
# ~/.android
|
||||
# - name: Cache Gradle
|
||||
# uses: actions/cache@v3
|
||||
# with:
|
||||
# path: |
|
||||
# ./mobile/build/
|
||||
# ./mobile/android/.gradle/
|
||||
# key: ${{ runner.os }}-flutter-${{ hashFiles('**/*.gradle*', 'pubspec.lock') }}
|
||||
# - name: Setup Android SDK
|
||||
# if: steps.android-sdk.outputs.cache-hit != 'true'
|
||||
# uses: android-actions/setup-android@v2
|
||||
# - name: AVD cache
|
||||
# uses: actions/cache@v3
|
||||
# id: avd-cache
|
||||
# with:
|
||||
# path: |
|
||||
# ~/.android/avd/*
|
||||
# ~/.android/adb*
|
||||
# key: avd-29
|
||||
# - name: create AVD and generate snapshot for caching
|
||||
# if: steps.avd-cache.outputs.cache-hit != 'true'
|
||||
# uses: reactivecircus/android-emulator-runner@v2.27.0
|
||||
# with:
|
||||
# working-directory: ./mobile
|
||||
# cores: 2
|
||||
# api-level: 29
|
||||
# arch: x86_64
|
||||
# profile: pixel
|
||||
# target: default
|
||||
# force-avd-creation: false
|
||||
# emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
# disable-animations: false
|
||||
# script: echo "Generated AVD snapshot for caching."
|
||||
# - name: Setup Flutter SDK
|
||||
# uses: subosito/flutter-action@v2
|
||||
# with:
|
||||
# channel: 'stable'
|
||||
# flutter-version: '3.7.3'
|
||||
# cache: true
|
||||
# - name: Run integration tests
|
||||
# uses: Wandalen/wretry.action@master
|
||||
# with:
|
||||
# action: reactivecircus/android-emulator-runner@v2.27.0
|
||||
# with: |
|
||||
# working-directory: ./mobile
|
||||
# cores: 2
|
||||
# api-level: 29
|
||||
# arch: x86_64
|
||||
# profile: pixel
|
||||
# target: default
|
||||
# force-avd-creation: false
|
||||
# emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
# disable-animations: true
|
||||
# script: |
|
||||
# flutter pub get
|
||||
# flutter test integration_test
|
||||
# attempt_limit: 3
|
||||
|
|
14
.gitignore
vendored
14
.gitignore
vendored
|
@ -1,3 +1,13 @@
|
|||
.DS_Store
|
||||
.vscode
|
||||
.idea
|
||||
.vscode/*
|
||||
!.vscode/launch.json
|
||||
.idea
|
||||
|
||||
docker/upload
|
||||
docker/library
|
||||
uploads
|
||||
coverage
|
||||
|
||||
mobile/gradle.properties
|
||||
mobile/openapi/pubspec.lock
|
||||
mobile/*.jks
|
||||
|
|
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
[submodule "mobile/.isar"]
|
||||
path = mobile/.isar
|
||||
url = https://github.com/isar/isar
|
||||
[submodule "server/test/assets"]
|
||||
path = server/test/assets
|
||||
url = https://github.com/immich-app/test-assets
|
23
.vscode/launch.json
vendored
Normal file
23
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"restart": true,
|
||||
"port": 9230,
|
||||
"name": "Immich Server",
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"localRoot": "${workspaceFolder}/server"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"restart": true,
|
||||
"port": 9231,
|
||||
"name": "Immich Microservices",
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"localRoot": "${workspaceFolder}/server"
|
||||
}
|
||||
]
|
||||
}
|
28
Makefile
28
Makefile
|
@ -1,29 +1,35 @@
|
|||
dev:
|
||||
rm -rf ./server/dist && docker-compose -f ./docker/docker-compose.dev.yml up --remove-orphans
|
||||
docker compose -f ./docker/docker-compose.dev.yml up --remove-orphans || make dev-down
|
||||
|
||||
dev-new:
|
||||
rm -rf ./server/dist && docker compose -f ./docker/docker-compose.dev.yml up --remove-orphans
|
||||
dev-down:
|
||||
docker compose -f ./docker/docker-compose.dev.yml down --remove-orphans
|
||||
|
||||
dev-update:
|
||||
rm -rf ./server/dist && docker-compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans
|
||||
docker compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans
|
||||
|
||||
dev-scale:
|
||||
rm -rf ./server/dist && docker-compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans
|
||||
docker compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans
|
||||
|
||||
stage:
|
||||
docker-compose -f ./docker/docker-compose.staging.yml up --build -V --remove-orphans
|
||||
docker compose -f ./docker/docker-compose.staging.yml up --build -V --remove-orphans
|
||||
|
||||
pull-stage:
|
||||
docker-compose -f ./docker/docker-compose.staging.yml pull
|
||||
docker compose -f ./docker/docker-compose.staging.yml pull
|
||||
|
||||
test-e2e:
|
||||
docker-compose -f ./docker/docker-compose.test.yml --env-file ./docker/.env.test -p immich-test-e2e up --renew-anon-volumes --abort-on-container-exit --exit-code-from immich-server-test --remove-orphans --build
|
||||
docker compose -f ./docker/docker-compose.test.yml up --renew-anon-volumes --abort-on-container-exit --exit-code-from immich-server --remove-orphans --build
|
||||
|
||||
prod:
|
||||
docker-compose -f ./docker/docker-compose.yml up --build -V --remove-orphans
|
||||
docker compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans
|
||||
|
||||
prod-scale:
|
||||
docker-compose -f ./docker/docker-compose.yml up --build -V --scale immich-server=3 --scale immich-microservices=3 --remove-orphans
|
||||
docker compose -f ./docker/docker-compose.prod.yml up --build -V --scale immich-server=3 --scale immich-microservices=3 --remove-orphans
|
||||
|
||||
api:
|
||||
cd ./server && npm run api:generate
|
||||
npm --prefix server run api:generate
|
||||
|
||||
sql:
|
||||
npm --prefix server run sql:generate
|
||||
|
||||
attach-server:
|
||||
docker exec -it docker_immich-server_1 sh
|
||||
|
|
9
NOTES.md
9
NOTES.md
|
@ -1,9 +0,0 @@
|
|||
# TODO
|
||||
|
||||
Server scenario with web
|
||||
|
||||
[ ] 1 user exist without admin right -> make admin on first check
|
||||
|
||||
[ ] 2 users exist without admin right -> ask user to choose which account will be the admin
|
||||
|
||||
[ X ] No users exist -> prompt signup form for Admin
|
|
@ -1,17 +0,0 @@
|
|||
# Deployment checklist for iOS/Android/Server
|
||||
|
||||
[ ] Up version in [mobile/pubspec.yml](/mobile/pubspec.yaml)
|
||||
|
||||
[ ] Up version in [docker/docker-compose.yml](/docker/docker-compose.yml) for `immich_server` service
|
||||
|
||||
[ ] Up version in [docker/docker-compose.gpu.yml](/docker/docker-compose.gpu.yml) for `immich_server` service
|
||||
|
||||
[ ] Up version in [docker/docker-compose.dev.yml](/docker/docker-compose.dev.yml) for `immich_server` service
|
||||
|
||||
[ ] Up version in [server/src/constants/server_version.constant.ts](/server/src/constants/server_version.constant.ts)
|
||||
|
||||
[ ] Up version in iOS Fastlane [/mobile/ios/fastlane/Fastfile](/mobile/ios/fastlane/Fastfile)
|
||||
|
||||
[ ] Add changelog to [Android Fastlane F-droid folder](/mobile/android/fastlane/metadata/android/en-US/changelogs)
|
||||
|
||||
All of the version should be the same.
|
320
README.md
320
README.md
|
@ -1,252 +1,122 @@
|
|||
<h1 align="center"> Immich </h1>
|
||||
<p align="center"> <b>High performance self-hosted photo and video backup solution.</b> </p>
|
||||
<p align="center">
|
||||
<img src="design/feature-panel.png" title="Immich Logo">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<p align="center">
|
||||
<br/>
|
||||
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-green.svg?color=3F51B5&style=for-the-badge&label=License&logoColor=000000&labelColor=ececec" alt="License: MIT"></a>
|
||||
<a href="https://github.com/alextran1502/immich"><img src="https://img.shields.io/github/stars/alextran1502/immich.svg?style=for-the-badge&logo=github&color=3F51B5&label=Stars&logoColor=000000&labelColor=ececec" alt="Star on Github"></a>
|
||||
<a href="https://immichci.little-home.net/viewType.html?buildTypeId=Immich_BuildAndroidAndGetArtifact&guest=1">
|
||||
<img src="https://img.shields.io/teamcity/http/immichci.little-home.net/s/Immich_BuildAndroidAndGetArtifact.svg?style=for-the-badge&label=Android&logo=teamcity&logoColor=000000&labelColor=ececec" alt="Android Build"/>
|
||||
</a>
|
||||
<a href="https://immichci.little-home.net/viewType.html?buildTypeId=Immich_BuildAndPublishIOSToTestFlight&guest=1">
|
||||
<img src="https://img.shields.io/teamcity/http/immichci.little-home.net/s/Immich_BuildAndPublishIOSToTestFlight.svg?style=for-the-badge&label=iOS&logo=teamcity&logoColor=000000&labelColor=ececec" alt="iOS Build"/>
|
||||
</a>
|
||||
<a href="https://actions-badge.atrox.dev/alextran1502/immich/goto?ref=main">
|
||||
<img alt="Build Status" src="https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Falextran1502%2Fimmich%2Fbadge%3Fref%3Dmain&style=for-the-badge&label=Github Action&logo=github&labelColor=ececec&logoColor=000000" />
|
||||
</a>
|
||||
<a href="https://discord.gg/D8JsnBEuKb">
|
||||
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Immich%20Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" atl="Immich Discord"/>
|
||||
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" alt="Discord"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="design/immich-logo.svg" width="150" title="Login With Custom URL">
|
||||
</p>
|
||||
<h3 align="center">Immich - High performance self-hosted photo and video backup solution</h3>
|
||||
<br/>
|
||||
<a href="https://immich.app">
|
||||
<img src="design/immich-screenshots.png" title="Main Screenshot">
|
||||
</a>
|
||||
<br/>
|
||||
<p align="center">
|
||||
<a href="README_ca_ES.md">Català</a>
|
||||
<a href="README_es_ES.md">Español</a>
|
||||
<a href="README_fr_FR.md">Français</a>
|
||||
<a href="README_it_IT.md">Italiano</a>
|
||||
<a href="README_ja_JP.md">日本語</a>
|
||||
<a href="README_ko_KR.md">한국어</a>
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
</p>
|
||||
|
||||
## Disclaimer
|
||||
|
||||
- ⚠️ The project is under **very active** development.
|
||||
- ⚠️ Expect bugs and breaking changes.
|
||||
- ⚠️ **Do not use the app as the only way to store your photos and videos.**
|
||||
- ⚠️ Always follow [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) backup plan for your precious photos and videos!
|
||||
|
||||
## Content
|
||||
|
||||
- [Official Documentation](https://immich.app/docs)
|
||||
- [Roadmap](https://github.com/orgs/immich-app/projects/1)
|
||||
- [Demo](#demo)
|
||||
- [Features](#features)
|
||||
- [Screenshots](#screenshots)
|
||||
- [Installation](#installation)
|
||||
- [Update](#update)
|
||||
- [Mobile App](#-mobile-app)
|
||||
- [Development](#development)
|
||||
- [Support](#support)
|
||||
- [Known Issues](#known-issues)
|
||||
- [Introduction](https://immich.app/docs/overview/introduction)
|
||||
- [Installation](https://immich.app/docs/install/requirements)
|
||||
- [Contribution Guidelines](https://immich.app/docs/overview/support-the-project)
|
||||
- [Support The Project](#support-the-project)
|
||||
|
||||
# Features
|
||||
## Documentation
|
||||
|
||||
> ⚠️ WARNING: **NOT READY FOR PRODUCTION! DO NOT USE TO STORE YOUR ASSETS**. This project is under heavy development, there will be continuous functions, features and api changes.
|
||||
You can find the main documentation, including installation guides, at https://immich.app/.
|
||||
|
||||
| Features | Mobile | Web |
|
||||
| - | - | - |
|
||||
| Upload and view videos and photos | Yes | Yes
|
||||
| Auto backup when the app is opened | Yes | N/A
|
||||
| Selective album(s) for backup | Yes | N/A
|
||||
| Download photos and videos to local device | Yes | Yes
|
||||
| Multi-user support | Yes | Yes
|
||||
| Album | Yes | Yes
|
||||
| Shared Albums | Yes | Yes
|
||||
| Quick navigation with draggable scrollbar | Yes | Yes
|
||||
| Support RAW (HEIC, HEIF, DNG, Apple ProRaw) | Yes | Yes
|
||||
| Metadata view (EXIF, map) | Yes | Yes
|
||||
| Search by metadata, objects and image tags | Yes | No
|
||||
| Administrative functions (user management) | N/A | Yes
|
||||
| Background backup | Android | N/A
|
||||
| Virtual scroll | N/A | Yes
|
||||
## Demo
|
||||
|
||||
You can access the web demo at https://demo.immich.app
|
||||
|
||||
<br/>
|
||||
For the mobile app, you can use `https://demo.immich.app/api` for the `Server Endpoint URL`
|
||||
|
||||
# Screenshots
|
||||
|
||||
### Mobile
|
||||
| | | | | |
|
||||
| - | - | - | - | - |
|
||||
| <img src="design/login-screen.png" width="150" title="Login With Custom URL"> <p align="center"> Login with custom URL </p> | <img src="design/backup-screen.png" width="150" title="Backup Setting Info"> <p align="center"> Backup Settings </p> | <img src="design/selective-backup-screen.png" width="150" title="Backup Setting Info"> <p align="center"> Backup selection </p> | <img src="design/home-screen.jpeg" width="150" title="Home Screen"> <p align="center"> Home Screen </p> | <img src="design/search-screen.jpeg" width="150" title="Curated Search Info"> <p align="center"> Curated search </p> |
|
||||
| <img src="design/shared-albums.png" width="150" title="Shared Albums"> <p align="center"> Shared albums </p> | <img src="design/nsc6.png" width="150" title="EXIF Info"> <p align="center"> EXIF info </p> | <img src="https://media.giphy.com/media/y8ZeaAigGmNvlSoKhU/giphy.gif" width="150" title="Loading ~4000 images/videos"> <p align="center"> Loading ~4000 images/videos </p> |
|
||||
|
||||
### Web
|
||||
| Home Dashboard | Image view |
|
||||
| - | - |
|
||||
|<img src="design/web-home.jpeg" width="100%" title="Home Dashboard"> | <img src="design/web-detail.jpeg" width="100%" title="Detail">|
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
# Project Details
|
||||
## 💾 System Requirements
|
||||
|
||||
- **OS**: Preferred unix-based operating system (Ubuntu, Debian, MacOS...etc).
|
||||
|
||||
- **RAM**: At least 2GB, preferred 4GB.
|
||||
|
||||
- **Core**: At least 2 cores, preferred 4 cores.
|
||||
|
||||
## 🔩 Technology Stack
|
||||
|
||||
There are several services that compose Immich:
|
||||
|
||||
1. **NestJs** - Backend of the application
|
||||
2. **SvelteKit** - Web frontend of the application
|
||||
3. **PostgreSQL** - Main database of the application
|
||||
4. **Redis** - For sharing websocket instance between docker instances and background tasks message queue.
|
||||
5. **Nginx** - Load balancing and optimized file uploading.
|
||||
6. **TensorFlow** - Object Detection (COCO SSD) and Image Classification (ImageNet).
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
# Installation
|
||||
|
||||
NOTE: When using a reverse proxy in front of Immich (such as NGINX), the reverse proxy might require extra configuration to allow large files to be uploaded (such as client_max_body_size in the case of NGINX).
|
||||
|
||||
## Testing One-step installation (not recommended for production)
|
||||
|
||||
> ⚠️ *This installation method is for evaluating Immich before futher customization to meet the users' needs.*
|
||||
|
||||
*Applicable system: Ubuntu, Debian, MacOS*
|
||||
|
||||
- In the shell, from the directory of your choice, run the following command:
|
||||
|
||||
```bash
|
||||
curl -o- https://raw.githubusercontent.com/immich-app/immich/main/install.sh | bash
|
||||
```bash title="Demo Credential"
|
||||
The credential
|
||||
email: demo@immich.app
|
||||
password: demo
|
||||
```
|
||||
|
||||
This script will download the `docker-compose.yml` file and the `.env` file, then populate the necessary information, and finally run the `docker-compose up` or `docker compose up` (based on your docker's version) command.
|
||||
|
||||
The web application will be available at `http://<machine-ip-address>:2283`, and the server URL for the mobile app will be `http://<machine-ip-address>:2283/api`.
|
||||
|
||||
The directory which is used to store the backup file is `./immich-app/immich-data`.
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
## Custom installation (Recommended)
|
||||
|
||||
### Step 1 - Download necessary files
|
||||
|
||||
- Create a directory called `immich-app` and cd into it.
|
||||
|
||||
- Get `docker-compose.yml`
|
||||
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/immich-app/immich/main/docker/docker-compose.yml
|
||||
```
|
||||
Spec: Free-tier Oracle VM - Amsterdam - 2.4Ghz quad-core ARM64 CPU, 24GB RAM
|
||||
```
|
||||
|
||||
- Get `.env`
|
||||
## Features
|
||||
|
||||
```bash
|
||||
wget -O .env https://raw.githubusercontent.com/immich-app/immich/main/docker/.env.example
|
||||
```
|
||||
| Features | Mobile | Web |
|
||||
| -------------------------------------------- | ------ | --- |
|
||||
| Upload and view videos and photos | Yes | Yes |
|
||||
| Auto backup when the app is opened | Yes | N/A |
|
||||
| Selective album(s) for backup | Yes | N/A |
|
||||
| Download photos and videos to local device | Yes | Yes |
|
||||
| Multi-user support | Yes | Yes |
|
||||
| Album and Shared albums | Yes | Yes |
|
||||
| Scrubbable/draggable scrollbar | Yes | Yes |
|
||||
| Support raw formats | Yes | Yes |
|
||||
| Metadata view (EXIF, map) | Yes | Yes |
|
||||
| Search by metadata, objects, faces, and CLIP | Yes | Yes |
|
||||
| Administrative functions (user management) | No | Yes |
|
||||
| Background backup | Yes | N/A |
|
||||
| Virtual scroll | Yes | Yes |
|
||||
| OAuth support | Yes | Yes |
|
||||
| API Keys | N/A | Yes |
|
||||
| LivePhoto/MotionPhoto backup and playback | Yes | Yes |
|
||||
| User-defined storage structure | Yes | Yes |
|
||||
| Public Sharing | No | Yes |
|
||||
| Archive and Favorites | Yes | Yes |
|
||||
| Global Map | Yes | Yes |
|
||||
| Partner Sharing | Yes | Yes |
|
||||
| Facial recognition and clustering | Yes | Yes |
|
||||
| Memories (x years ago) | Yes | Yes |
|
||||
| Offline support | Yes | No |
|
||||
| Read-only gallery | Yes | Yes |
|
||||
| Stacked Photos | Yes | Yes |
|
||||
|
||||
### Step 2 - Populate .env file with custom information
|
||||
## Support the project
|
||||
|
||||
<a href="https://github.com/immich-app/immich/blob/main/docker/.env.example" target="_blank"><b>See the example <code>.env</code> file</b></a>
|
||||
I've committed to this project, and I will not stop. I will keep updating the docs, adding new features, and fixing bugs. But I can't do it alone. So I need your help to give me additional motivation to keep going.
|
||||
|
||||
* Populate custom database information if necessary.
|
||||
* Populate `UPLOAD_LOCATION` as prefered location for storing backup assets.
|
||||
* Populate a secret value for `JWT_SECRET`, you can use this command: `openssl rand -base64 128`
|
||||
* [Optional] Populate Mapbox value to use reverse geocoding.
|
||||
* [Optional] Populate `TZ` as your timezone, default is `Etc/UTC`.
|
||||
As our hosts in the [selfhosted.show - In the episode 'The-organization-must-not-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418) said, this is a massive undertaking of what the team and I are doing. And I would love to someday be able to do this full-time, and I am asking for your help to make that happen.
|
||||
|
||||
### Step 3 - Start the containers
|
||||
If you feel like this is the right cause and the app is something you are seeing yourself using for a long time, please consider supporting the project with the option below.
|
||||
|
||||
- Run `docker-compose up` or `docker compose up` (based on your docker's version)
|
||||
### Donation
|
||||
|
||||
### Step 4 - Register admin user
|
||||
- [Monthly donation](https://github.com/sponsors/alextran1502) via GitHub Sponsors
|
||||
- [One-time donation](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) via GitHub Sponsors
|
||||
- [Librepay](https://liberapay.com/alex.tran1502/)
|
||||
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
|
||||
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
|
||||
- ZCash: u1smm4wvqegcp46zss2jf5xptchgeczp4rx7a0wu3mermf2wxahm26yyz5w9mw3f2p4emwlljxjumg774kgs8rntt9yags0whnzane4n67z4c7gppq4yyvcj404ne3r769prwzd9j8ntvqp44fa6d67sf7rmcfjmds3gmeceff4u8e92rh38nd30cr96xw6vfhk6scu4ws90ldzupr3sz
|
||||
|
||||
- Navigate to the web at `http://<machine-ip-address>:2283` and follow the prompts to register admin user.
|
||||
<p align="center">
|
||||
<img src="design/admin-registration-form.png" width="300" title="Admin Registration">
|
||||
</p>
|
||||
|
||||
- You can add and manage users from the administration page.
|
||||
<p align="center">
|
||||
<img src="design/admin-interface.png" width="500" title="Admin User Management">
|
||||
</p>
|
||||
|
||||
### Step 5 - Access the mobile app
|
||||
|
||||
- Login the mobile app with the server endpoint URL at `http://<machine-ip-address>:2283/api`
|
||||
<p align="center">
|
||||
<img src="design/login-screen.jpeg" width="250" title="Example login screen">
|
||||
</p>
|
||||
|
||||
<br/>
|
||||
|
||||
## Update
|
||||
|
||||
If you have installed, you can update the application by navigate to the directory that contains the `docker-compose.yml` file and run the following command:
|
||||
|
||||
```bash
|
||||
docker-compose pull && docker-compose up -d
|
||||
```
|
||||
|
||||
# Mobile app
|
||||
|
||||
| F-Droid | Google Play | iOS |
|
||||
| - | - | - |
|
||||
| <a href="https://f-droid.org/packages/app.alextran.immich"><img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="80"></a> | <p align="left"> <a href="https://play.google.com/store/apps/details?id=app.alextran.immich"><img src="design/google-play-qr-code.png" width="200" title="Google Play Store"></a> <p/> | <p align="left"> <a href="https://apps.apple.com/us/app/immich/id1613945652"><img src="design/ios-qr-code.png" width="200" title="Apple App Store"></a> <p/> |
|
||||
|
||||
> *The Play/App Store version might be lagging behind the latest release due to the review process.*
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
# Development
|
||||
|
||||
The development environment can be started from the root of the project after populating the `.env` file with the command:
|
||||
|
||||
```bash
|
||||
make dev # required Makefile installed on the system.
|
||||
```
|
||||
|
||||
All servers and web container are hot reload for quick feedback loop.
|
||||
|
||||
## Note for developers
|
||||
### 1 - OpenAPI
|
||||
OpenAPI is used to generate the client (Typescript, Dart) SDK. `openapi-generator-cli` can be installed [here](https://openapi-generator.tech/docs/installation/). When you add a new or modify an existing endpoint, you must run the generate command below to update the client SDK.
|
||||
|
||||
```bash
|
||||
npm run api:generate # Run from server directory
|
||||
```
|
||||
You can find the generated client SDK in the [`web/src/api`](web/src/api) for Typescript SDK and [`mobile/openapi`](mobile/openapi) for Dart SDK.
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
# Support
|
||||
|
||||
If you like the app, find it helpful, and want to support me to offset the cost of publishing to AppStores, you can sponsor the project with [**one time**](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) or monthly donation from [**Github Sponsor**](https://github.com/sponsors/alextran1502).
|
||||
|
||||
You can also donate using crypto currency with the following addresses:
|
||||
|
||||
<p align="" style="display: flex; place-items: center; gap: 15px" title="Bitcoin(BTC)"><img src="design/bitcoin.png" width="25" title="Bitcoin"> <b>Bitcoin</b>: <code>1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX</code></p>
|
||||
|
||||
<p align="" style="display: flex; place-items: center; gap: 15px" title="Cardano(ADA)"> <img src="design/cardano.png" width="30" title="Cardano"> <b>Cardano</b>: <code>addr1qyy567vqhqrr3p7vpszr5p264gw89sqcwts2z8wqy4yek87cdmy79zazyjp7tmwhkluhk3krvslkzfvg0h43tytp3f5q49nycc</code> </p>
|
||||
|
||||
|
||||
This is also a meaningful way to give me motivation and encouragement to continue working on the app.
|
||||
|
||||
Cheers! 🎉
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
# Known Issues
|
||||
|
||||
## TensorFlow Build Issue
|
||||
|
||||
*This is a known issue for incorrect Proxmox setup*
|
||||
|
||||
TensorFlow doesn't run with older CPU architecture, it requires a CPU with AVX and AVX2 instruction set. If you encounter the error `illegal instruction core dump` when running the docker-compose command above, check for your CPU flags with the command and make sure you see `AVX` and `AVX2`:
|
||||
|
||||
```bash
|
||||
more /proc/cpuinfo | grep flags
|
||||
```
|
||||
|
||||
If you are running virtualization in Proxmox, the VM doesn't have the flag enabled.
|
||||
|
||||
You need to change the CPU type from `kvm64` to `host` under VMs hardware tab.
|
||||
|
||||
`Hardware > Processors > Edit > Advanced > Type (dropdown menu) > host`
|
||||
## Contributors
|
||||
<a href="https://github.com/alextran1502/immich/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=immich-app/immich" width="100%"/>
|
||||
</a>
|
||||
|
|
114
README_ca_ES.md
Normal file
114
README_ca_ES.md
Normal file
|
@ -0,0 +1,114 @@
|
|||
<p align="center">
|
||||
<br/>
|
||||
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-green.svg?color=3F51B5&style=for-the-badge&label=Llicència&logoColor=000000&labelColor=ececec" alt="Llicència: MIT"></a>
|
||||
<a href="https://discord.gg/D8JsnBEuKb">
|
||||
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" atl="Discord"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="design/immich-logo.svg" width="150" title="Iniciar sessió amb URL personalitzada">
|
||||
</p>
|
||||
<h3 align="center">Immich - Solució de còpia de seguretat d'alta rendiment per a fotos i vídeos auto-allotjada</h3>
|
||||
<br/>
|
||||
<a href="https://immich.app">
|
||||
<img src="design/immich-screenshots.png" title="Captura de pantalla principal">
|
||||
</a>
|
||||
<br/>
|
||||
<p align="center">
|
||||
<a href="README.md">English</a>
|
||||
<a href="README_ca_ES.md">Español</a>
|
||||
<a href="README_fr_FR.md">Français</a>
|
||||
<a href="README_it_IT.md">Italiano</a>
|
||||
<a href="README_ja_JP.md">日本語</a>
|
||||
<a href="README_ko_KR.md">한국어</a>
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
</p>
|
||||
|
||||
## Avís legal
|
||||
|
||||
- ⚠️ El projecte està en desenvolupament **molt actiu**.
|
||||
- ⚠️ Espereu errors i canvis que poden trencar coses.
|
||||
- ⚠️ **No utilitzeu l'aplicació com a única manera de guardar les vostres fotos i vídeos!**
|
||||
|
||||
## Contingut
|
||||
|
||||
- [Documentació oficial](https://immich.app/docs)
|
||||
- [Mapa de ruta](https://github.com/orgs/immich-app/projects/1)
|
||||
- [Demo](#demo)
|
||||
- [Funcionalitats](#funcionalitats)
|
||||
- [Introducció](https://immich.app/docs/overview/introduction)
|
||||
- [Instal·lació](https://immich.app/docs/install/requirements)
|
||||
- [Directrius de contribució](https://immich.app/docs/overview/support-the-project)
|
||||
- [Donar suport al projecte](#suportar-el-projecte)
|
||||
|
||||
## Documentació
|
||||
|
||||
Podeu trobar la documentació principal, incloent les guies d'instal·lació, a https://immich.app/.
|
||||
|
||||
## Demo
|
||||
|
||||
Podeu accedir a la demostració web a https://demo.immich.app
|
||||
|
||||
Per a l'aplicació mòbil, podeu utilitzar `https://demo.immich.app/api` com a "URL de punt final del servidor".
|
||||
|
||||
```bash title="Credencials de la demo"
|
||||
Les credencials
|
||||
email: demo@immich.app
|
||||
contrasenya: demo
|
||||
```
|
||||
```
|
||||
Spec: Free-tier Oracle VM - Amsterdam - 2.4Ghz quad-core ARM64 CPU, 24GB RAM
|
||||
```
|
||||
|
||||
# Funcionalitats
|
||||
|
||||
| Característiques | Mòbil | Web |
|
||||
| -------------------------------------------- | ------ | --- |
|
||||
| Pujar i veure vídeos i fotos | Sí | Sí |
|
||||
| Còpia de seguretat automàtica en obrir l'aplicació | Sí | N/A |
|
||||
| Selecció d'àlbums per a la còpia de seguretat | Sí | N/A |
|
||||
| Descarregar fotos i vídeos a l'aparell local | Sí | Sí |
|
||||
| Suport per a múltiples usuaris | Sí | Sí |
|
||||
| Àlbums i àlbums compartits | Sí | Sí |
|
||||
| Barra de desplaçament amb funció de rasclet/arrossegament | Sí | Sí |
|
||||
| Suport per a formats raw | Sí | Sí |
|
||||
| Visualització de metadades (EXIF, mapa) | Sí | Sí |
|
||||
| Cerca per metadades, objectes, cares i CLIP | Sí | Sí |
|
||||
| Funcions administratives (gestió d'usuaris) | No | Sí |
|
||||
| Còpia de seguretat en segon pla | Sí | N/A |
|
||||
| Desplaçament virtual | Sí | Sí |
|
||||
| Suport per a OAuth | Sí | Sí |
|
||||
| Claus d'API | N/A | Sí |
|
||||
| Còpia de seguretat i reproducció de LivePhoto | iOS | Sí |
|
||||
| Estructura d'emmagatzematge definida per l'usuari | Sí | Sí |
|
||||
| Compartició pública | No | Sí |
|
||||
| Arxiu i preferits | Sí | Sí |
|
||||
| Mapa global | No | Sí |
|
||||
| Compartició amb associats | Sí | Sí |
|
||||
| Reconeixement facial i agrupament | Sí | Sí |
|
||||
| Records (fa x anys) | Sí | Sí |
|
||||
| Suport fora de línia | Sí | No |
|
||||
| Galeria de només lectura | Sí | Sí |
|
||||
|
||||
# Donar suport al projecte
|
||||
|
||||
M'he compromès amb aquest projecte i no em detindré. Continuaré actualitzant la documentació, afegint noves funcionalitats i solucionant errors. Però no ho puc fer sol. Per això, necessito la vostra ajuda per donar-me motivació addicional per seguir endavant.
|
||||
|
||||
Com van dir els nostres amfitrions a l'episodi [selfhosted.show - 'The-organization-must-not-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418), això és una tasca enorme del que l'equip i jo estem fent. I m'encantaria poder dedicar-m'hi a temps complet, per la qual cosa us demano la vostra ajuda per fer-ho possible.
|
||||
|
||||
Si creieu que aquesta és una causa justa i l'aplicació és alguna cosa que us veieu utilitzant durant molt de temps, considereu donar suport al projecte amb alguna de les opcions següents.
|
||||
|
||||
## Donació
|
||||
|
||||
- [Donació mensual](https://github.com/sponsors/alextran1502) a través de GitHub Sponsors
|
||||
- [Donació única](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) a través de GitHub Sponsors
|
||||
- [Librepay](https://liberapay.com/alex.tran1502/)
|
||||
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
|
||||
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
|
||||
- ZCash: u1smm4wvqegcp46zss2jf5xptchgeczp4rx7a0wu3mermf2wxahm26yyz5w9mw3f2p4emwlljxjumg774kgs8rntt9yags0whnzane4n67z4c7gppq4yyvcj404ne3r769prwzd9j8ntvqp44fa6d67sf7rmcfjmds3gmeceff4u8e92rh38nd30cr96xw6vfhk6scu4ws90ldzupr3sz
|
122
README_de_DE.md
Normal file
122
README_de_DE.md
Normal file
|
@ -0,0 +1,122 @@
|
|||
<p align="center">
|
||||
<br/>
|
||||
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-green.svg?color=3F51B5&style=for-the-badge&label=License&logoColor=000000&labelColor=ececec" alt="Lizenz: MIT"></a>
|
||||
<a href="https://discord.gg/D8JsnBEuKb">
|
||||
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" alt="Discord"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="design/immich-logo.svg" width="150" title="Login mit eigener URL">
|
||||
</p>
|
||||
<h3 align="center">Immich - Hoch performante, selbst gehostete Backup-Lösung für Fotos und Videos</h3>
|
||||
<br/>
|
||||
<a href="https://immich.app">
|
||||
<img src="design/immich-screenshots.png" title="Haupt-Screenshot">
|
||||
</a>
|
||||
<br/>
|
||||
<p align="center">
|
||||
<a href="README.md">English</a>
|
||||
<a href="README_ca_ES.md">Català</a>
|
||||
<a href="README_es_ES.md">Español</a>
|
||||
<a href="README_fr_FR.md">Français</a>
|
||||
<a href="README_it_IT.md">Italiano</a>
|
||||
<a href="README_ja_JP.md">日本語</a>
|
||||
<a href="README_ko_KR.md">한국어</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
</p>
|
||||
|
||||
## Warnung
|
||||
|
||||
- ⚠️ Das Projekt befindet sich in **sehr aktiver** Entwicklung.
|
||||
- ⚠️ Erwarte Fehler und Änderungen mit Breaking-Changes.
|
||||
- ⚠️ **Nutze die App auf keinen Fall als einziges Speichermedium für deine Fotos und Videos.**
|
||||
- ⚠️ Befolge immer die [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) Backup-Regel für deine wertvollen Fotos und Videos!
|
||||
|
||||
## Inhalt
|
||||
|
||||
- [Offizielle Dokumentation](https://immich.app/docs)
|
||||
- [Roadmap](https://github.com/orgs/immich-app/projects/1)
|
||||
- [Demo](#demo)
|
||||
- [Funktionen](#funktionen)
|
||||
- [Einführung](https://immich.app/docs/overview/introduction)
|
||||
- [Installation](https://immich.app/docs/install/requirements)
|
||||
- [Beitragsrichtlinien](https://immich.app/docs/overview/support-the-project)
|
||||
- [Unterstütze das Projekt](#unterstütze-das-projekt)
|
||||
|
||||
## Dokumentation
|
||||
|
||||
Die Hauptdokumentation, inklusive Installationsanleitungen, ist unter https://immich.app zu finden.
|
||||
|
||||
## Demo
|
||||
|
||||
Die Web-Demo kannst Du unter https://demo.immich.app finden.
|
||||
|
||||
Für die Handy-App kannst Du `https://demo.immich.app/api` als `Server Endpoint URL` angeben.
|
||||
|
||||
```bash title="Demo Credential"
|
||||
Die Anmeldedaten
|
||||
email: demo@immich.app
|
||||
passwort: demo
|
||||
```
|
||||
|
||||
```
|
||||
Spec: Free-tier Oracle VM - Amsterdam - 2.4Ghz quad-core ARM64 CPU, 24GB RAM
|
||||
```
|
||||
|
||||
## Funktionen
|
||||
|
||||
| Funktionen | Mobil | Web |
|
||||
| ---------------------------------------------------- | ------ | ----- |
|
||||
| Fotos & Videos hochladen und ansehen | Ja | Ja |
|
||||
| Automatisches Backup wenn die App geöffnet ist | Ja | n. a. |
|
||||
| Selektive Auswahl von Alben zum Sichern | Ja | n. a. |
|
||||
| Fotos und Videos auf das Gerät herunterladen | Ja | Ja |
|
||||
| Unterstützt mehrere Benutzer | Ja | Ja |
|
||||
| Album und geteilte Alben | Ja | Ja |
|
||||
| Scrollleiste | Ja | Ja |
|
||||
| Unterstützt RAW Formate | Ja | Ja |
|
||||
| Metadaten anzeigen (EXIF, Karte) | Ja | Ja |
|
||||
| Suchen nach Metadaten, Objekten, Gesichtern und CLIP | Ja | Ja |
|
||||
| Administrative Funktionen (Benutzerverwaltung) | Nein | Ja |
|
||||
| Backup im Hintergrund | Ja | n. a. |
|
||||
| Virtuelles Scrollen | Ja | Ja |
|
||||
| OAuth Unterstützung | Ja | Ja |
|
||||
| API-Schlüssel | n. a. | Ja |
|
||||
| LivePhoto/MotionPhoto Backup und Wiedergabe | Ja | Ja |
|
||||
| Benutzerdefinierte Speicherstruktur | Ja | Ja |
|
||||
| Öffentliches Teilen | Nein | Ja |
|
||||
| Archive und Favoriten | Ja | Ja |
|
||||
| Globale Karte | Ja | Ja |
|
||||
| Teilen mit Partner | Ja | Ja |
|
||||
| Gesichtserkennung und Gruppierung | Ja | Ja |
|
||||
| Rückblicke (heute vor x Jahren) | Ja | Ja |
|
||||
| Offline Unterstützung | Ja | Nein |
|
||||
| Schreibgeschützte Gallerie | Ja | Ja |
|
||||
| Gestapelte Bilder | Ja | Ja |
|
||||
|
||||
## Unterstütze das Projekt
|
||||
|
||||
Ich habe mich diesem Projekt verpflichtet und werde nicht aufgeben. Ich werde die Dokumentation weiter aktualisieren, neue Funktionen hinzufügen und Fehler beheben. Allerdings kann ich das nicht alleine schaffen. Daher brauche ich Eure Unterstützung, um mir zusätzliche Motivation zu geben, weiterzumachen.
|
||||
|
||||
Wie unsere Gastgeber in der [selfhosted.show - In der Episode 'The-organization-must-not-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418) gesagt haben, ist dies ein riesiges Unterfangen, welchem das Team und ich uns annehmen. In Zukunft würde ich liebend gerne Vollzeit an dem Projekt arbeiten und bitte daher um Eure Unterstützung.
|
||||
|
||||
Wenn Du denkst, dass dies die richtige Sache ist und dich selbst die App für eine längere Zeit nutzen siehst, dann denke bitte darüber nach, das Projekt mit einer der unten aufgelisteten Optionen zu unterstützen.
|
||||
|
||||
### Spenden
|
||||
|
||||
- [Monatliche Spende](https://github.com/sponsors/immich-app) via GitHub Sponsors
|
||||
- [Einmalige Spende](https://github.com/sponsors/immich-app?frequency=one-time&sponsor=immich-app) via GitHub Sponsors
|
||||
- [Librepay](https://liberapay.com/alex.tran1502/)
|
||||
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
|
||||
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
|
||||
- ZCash: u1smm4wvqegcp46zss2jf5xptchgeczp4rx7a0wu3mermf2wxahm26yyz5w9mw3f2p4emwlljxjumg774kgs8rntt9yags0whnzane4n67z4c7gppq4yyvcj404ne3r769prwzd9j8ntvqp44fa6d67sf7rmcfjmds3gmeceff4u8e92rh38nd30cr96xw6vfhk6scu4ws90ldzupr3sz
|
||||
|
||||
## Mitwirkende
|
||||
<a href="https://github.com/alextran1502/immich/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=immich-app/immich" width="100%"/>
|
||||
</a>
|
115
README_es_ES.md
Normal file
115
README_es_ES.md
Normal file
|
@ -0,0 +1,115 @@
|
|||
<p align="center">
|
||||
<br/>
|
||||
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-green.svg?color=3F51B5&style=for-the-badge&label=License&logoColor=000000&labelColor=ececec" alt="Licencia: MIT"></a>
|
||||
<a href="https://discord.gg/D8JsnBEuKb">
|
||||
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" atl="Discord"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="design/immich-logo.svg" width="150" title="Iniciar sesión con URL personalizada">
|
||||
</p>
|
||||
<h3 align="center">Immich: Una solución Self-Hosted de copia de seguridad de fotos y videos de alto rendimiento</h3>
|
||||
<br/>
|
||||
<a href="https://immich.app">
|
||||
<img src="design/immich-screenshots.png" title="Captura de pantalla principal">
|
||||
</a>
|
||||
<br/>
|
||||
<p align="center">
|
||||
<a href="README.md">English</a>
|
||||
<a href="README_ca_ES.md">Català</a>
|
||||
<a href="README_fr_FR.md">Français</a>
|
||||
<a href="README_it_IT.md">Italiano</a>
|
||||
<a href="README_ja_JP.md">日本語</a>
|
||||
<a href="README_ko_KR.md">한국어</a>
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
</p>
|
||||
|
||||
## Descargo de responsabilidad
|
||||
|
||||
- ⚠️ El proyecto está en **desarrollo muy activo**.
|
||||
- ⚠️ Es probable que haya errores y cambios disruptivos.
|
||||
- ⚠️ **¡No utilices la aplicación como única forma de almacenar tus fotos y videos!**
|
||||
|
||||
## Contenido
|
||||
|
||||
- [Documentación oficial](https://immich.app/docs)
|
||||
- [Hoja de ruta](https://github.com/orgs/immich-app/projects/1)
|
||||
- [Demostración](#demo)
|
||||
- [Funciones](#features)
|
||||
- [Introducción](https://immich.app/docs/overview/introduction)
|
||||
- [Instalación](https://immich.app/docs/install/requirements)
|
||||
- [Directrices para contribuir](https://immich.app/docs/overview/support-the-project)
|
||||
- [Apoya el proyecto](#support-the-project)
|
||||
|
||||
## Documentación
|
||||
|
||||
Puedes encontrar la documentación principal, incluidas las guías de instalación, en <https://immich.app/>.
|
||||
|
||||
## Demostración
|
||||
|
||||
Puedes acceder a la demostración web en <https://demo.immich.app>
|
||||
|
||||
Para la aplicación móvil, puedes usar `https://demo.immich.app/api` como `URL de la terminal del servidor`.
|
||||
|
||||
```bash title="Credenciales de la demostración"
|
||||
Las credenciales son
|
||||
correo electrónico: demo@immich.app
|
||||
contraseña: demo
|
||||
```
|
||||
|
||||
```bash
|
||||
Especificaciones: VM de nivel gratuito de Oracle - Ámsterdam - CPU ARM64 de cuatro núcleos a 2.4 GHz, 24 GB de RAM
|
||||
```
|
||||
|
||||
## Funcionalidades
|
||||
|
||||
| Funcionalidades | Móvil | Web |
|
||||
| ----------------------------------------------------- | ------ | --- |
|
||||
| Cargar y ver videos y fotos | Sí | Sí |
|
||||
| Copia de seguridad automática al abrir la aplicación | Sí | N/D |
|
||||
| Álbum(es) selectivo(s) para copia de seguridad | Sí | N/D |
|
||||
| Descargar fotos y videos al dispositivo local | Sí | Sí |
|
||||
| Soporte multiusuario | Sí | Sí |
|
||||
| Álbum y álbumes compartidos | Sí | Sí |
|
||||
| Barra de desplazamiento con función de búsqueda | Sí | Sí |
|
||||
| Soporte para formatos RAW | Sí | Sí |
|
||||
| Visualización de metadatos (EXIF, map) | Sí | Sí |
|
||||
| Búsqueda por metadatos, objetos, rostros y CLIP | Sí | Sí |
|
||||
| Funciones administrativas (gestión de usuarios) | No | Sí |
|
||||
| Copia de seguridad en segundo plano | Sí | N/D |
|
||||
| Desplazamiento virtual | Sí | Sí |
|
||||
| Soporte de OAuth | Sí | Sí |
|
||||
| Claves de API | N/D | Sí |
|
||||
| Copia de seguridad y reproducción de LivePhoto | iOS | Sí |
|
||||
| Estructura de almacenamiento definida por el usuario | Sí | Sí |
|
||||
| Compartir públicamente | No | Sí |
|
||||
| Archivar y marcar como favorito | Sí | Sí |
|
||||
| Mapa global | No | Sí |
|
||||
| Compartir con colaboradores | Sí | Sí |
|
||||
| Reconocimiento facial y agrupación | Sí | Sí |
|
||||
| Recuerdos (hace x años) | Sí | Sí |
|
||||
| Soporte sin conexión | Sí | No |
|
||||
| Galería de solo lectura | Sí | Sí |
|
||||
|
||||
## Apoya el proyecto
|
||||
|
||||
Me he comprometido con este proyecto, y no me detendré. Continuaré actualizando la documentación, agregando nuevas funcionalidades y corrigiendo errores. Pero no puedo hacerlo solo. Por eso, necesito tu ayuda para darme una motivación adicional para seguir adelante.
|
||||
|
||||
Como dijeron nuestros anfitriones en [selfhosted.show - En el episodio 'The-organization-must-not-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418), esto es una gran tarea de lo que el equipo y yo estamos haciendo. Y me encantaría poder dedicarme a esto a tiempo completo algún día, así que te pido tu ayuda para que eso sea posible.
|
||||
|
||||
Si consideras que esta es una causa justa y la aplicación es algo que te gustaría usar durante mucho tiempo, por favor, considera apoyar el proyecto con las siguientes opciones.
|
||||
|
||||
## Donación
|
||||
|
||||
- [Donación mensual](https://github.com/sponsors/alextran1502) a través de GitHub Sponsors
|
||||
- [Donación única](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) a través de GitHub Sponsors
|
||||
- [Librepay](https://liberapay.com/alex.tran1502/)
|
||||
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
|
||||
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
|
||||
- ZCash: u1smm4wvqegcp46zss2jf5xptchgeczp4rx7a0wu3mermf2wxahm26yyz5w9mw3f2p4emwlljxjumg774kgs8rntt9yags0whnzane4n67z4c7gppq4yyvcj404ne3r769prwzd9j8ntvqp44fa6d67sf7rmcfjmds3gmeceff4u8e92rh38nd30cr96xw6vfhk6scu4ws90ldzupr3sz
|
116
README_fr_FR.md
Normal file
116
README_fr_FR.md
Normal file
|
@ -0,0 +1,116 @@
|
|||
<p align="center">
|
||||
<br/>
|
||||
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-green.svg?color=3F51B5&style=for-the-badge&label=License&logoColor=000000&labelColor=ececec" alt="License: MIT"></a>
|
||||
<a href="https://discord.gg/D8JsnBEuKb">
|
||||
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" atl="Discord"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="design/immich-logo.svg" width="150" title="Login With Custom URL">
|
||||
</p>
|
||||
<h3 align="center">Immich - Solution de sauvegarde performante et auto-hébergée des photos et des vidéos</h3>
|
||||
<br/>
|
||||
<a href="https://immich.app">
|
||||
<img src="design/immich-screenshots.png" title="Main Screenshot">
|
||||
</a>
|
||||
<br/>
|
||||
<p align="center">
|
||||
<a href="README.md">English</a>
|
||||
<a href="README_ca_ES.md">Català</a>
|
||||
<a href="README_es_ES.md">Español</a>
|
||||
<a href="README_it_IT.md">Italiano</a>
|
||||
<a href="README_ja_JP.md">日本語</a>
|
||||
<a href="README_ko_KR.md">한국어</a>
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
</p>
|
||||
|
||||
## Clause de non-responsabilité
|
||||
|
||||
- ⚠️ Le projet est en **très fort** développement.
|
||||
- ⚠️ Attendez-vous à rencontrer des bugs et des changements importants.
|
||||
- ⚠️ **N'utilisez pas cette application comme seule façon de sauvegarder vos photos et vos vidéos.**
|
||||
- ⚠️ Ayez toujours un plan de sauvegarde en [3-2-1](https://www.seagate.com/fr/fr/blog/what-is-a-3-2-1-backup-strategy/) pour vos précieuses photos et vidéos !
|
||||
|
||||
## Sommaire
|
||||
|
||||
- [Documentation officielle](https://immich.app/docs)
|
||||
- [Feuille de route](https://github.com/orgs/immich-app/projects/1)
|
||||
- [Démo](#demo)
|
||||
- [Fonctionnalités](#features)
|
||||
- [Introduction](https://immich.app/docs/overview/introduction)
|
||||
- [Installation](https://immich.app/docs/install/requirements)
|
||||
- [Contribution](https://immich.app/docs/overview/support-the-project)
|
||||
- [Soutenir le projet](#support-the-project)
|
||||
|
||||
## Documentation
|
||||
|
||||
Vous pouvez trouver la documentation principale ainsi que les guides d'installation sur https://immich.app/.
|
||||
|
||||
## Démo
|
||||
|
||||
Vous pouvez accéder à la démo Web sur https://demo.immich.app
|
||||
|
||||
Pour l'application mobile, vous pouvez utiliser `https://demo.immich.app/api` dans le champ 'URL du point d'accès au serveur'
|
||||
|
||||
```bash title="Demo Credential"
|
||||
Les identifiants
|
||||
email: demo@immich.app
|
||||
mot de passe: demo
|
||||
```
|
||||
|
||||
```
|
||||
Caractéristiques: Plan gratuit Oracle VM - Amsterdam - 2.4Ghz quatre-cœurs ARM64 CPU, 24GB RAM
|
||||
```
|
||||
|
||||
# Fonctionnalités
|
||||
|
||||
| Fonctionnalités | Mobile | Web |
|
||||
| ---------------------------------------------------------------- | ------ | --- |
|
||||
| Téléverser et voir les vidéos et photos | Oui | Oui |
|
||||
| Sauvegarde automatique quand l'application est ouverte | Oui | N/A |
|
||||
| Sélection des albums à sauvegarder | Oui | N/A |
|
||||
| Télécharger les photos et les vidéos sur l'appareil | Oui | Oui |
|
||||
| Support multi-utilisateur | Oui | Oui |
|
||||
| Albums et albums partagés | Oui | Oui |
|
||||
| Barre de défilement mobile | Oui | Oui |
|
||||
| Support des formats raw | Oui | Oui |
|
||||
| Vue sur les métadonnées (EXIF, carte) | Oui | Oui |
|
||||
| Rechercher par métadonnées, objets, faces et CLIP | Oui | Oui |
|
||||
| Fonctions d'administration (gestion des utilisateurs) | Non | Oui |
|
||||
| Sauvegarde en tâche de fond | Oui | N/A |
|
||||
| Défilement virtuel | Oui | Oui |
|
||||
| Support de l'OAuth | Oui | Oui |
|
||||
| Clés d'API | N/A | Oui |
|
||||
| Sauvegarde et lecture des LivePhotos | iOS | Oui |
|
||||
| Structure de stockage définissable | Oui | Oui |
|
||||
| Partage public | Non | Oui |
|
||||
| Archives et favoris | Oui | Oui |
|
||||
| Carte globale | Non | Oui |
|
||||
| Partage entre utilisateurs | Oui | Oui |
|
||||
| Reconnaissance et regroupement facial | Oui | Oui |
|
||||
| Souvenirs (il y a x années) | Oui | Oui |
|
||||
| Support hors-ligne | Oui | Non |
|
||||
| Gallerie en lecture seule | Oui | Oui |
|
||||
|
||||
# Soutenir le projet
|
||||
|
||||
Je me suis engagé sur ce projet, et je ne compte pas m'arrêter. Je continuerai à mettre à jour les documentations, d'ajouter de nouvelles fonctionnalités et de résoudre des bugs. Mais je ne peux pas faire cela seul. Donc j'ai besoin de votre aide pour me donner encore plus de motivation et ainsi continuer.
|
||||
|
||||
Comme l'ont dit nos hôtes dans le [selfhosted.show - Dans l'épisode 'The-organization-must-not-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418), c'est un travail colossal ce que l'équipe et moi faisons. J'aimerais un jour être capable de faire ça à temps plein, c'est pourquoi je vous demande votre aide pour rendre cela possible.
|
||||
|
||||
Si vous estimez que c'est pour la bonne cause et que vous prévoyez d'utiliser l'application pour un moment, s'il-vous-plaît, pensez à soutenir le projet avec les moyens ci-dessous.
|
||||
|
||||
## Donation
|
||||
|
||||
- [Donation mensuelle](https://github.com/sponsors/alextran1502) via GitHub Sponsors
|
||||
- [Donation occasionnelle](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) via GitHub Sponsors
|
||||
- [Librepay](https://liberapay.com/alex.tran1502/)
|
||||
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
|
||||
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
|
||||
- ZCash: u1smm4wvqegcp46zss2jf5xptchgeczp4rx7a0wu3mermf2wxahm26yyz5w9mw3f2p4emwlljxjumg774kgs8rntt9yags0whnzane4n67z4c7gppq4yyvcj404ne3r769prwzd9j8ntvqp44fa6d67sf7rmcfjmds3gmeceff4u8e92rh38nd30cr96xw6vfhk6scu4ws90ldzupr3sz
|
116
README_it_IT.md
Normal file
116
README_it_IT.md
Normal file
|
@ -0,0 +1,116 @@
|
|||
<p align="center">
|
||||
<br/>
|
||||
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-green.svg?color=3F51B5&style=for-the-badge&label=License&logoColor=000000&labelColor=ececec" alt="License: MIT"></a>
|
||||
<a href="https://discord.gg/D8JsnBEuKb">
|
||||
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" atl="Discord"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="design/immich-logo.svg" width="150" title="Login With Custom URL">
|
||||
</p>
|
||||
<h3 align="center">Immich - Soluzione self-hosted ad alte prestazioni per backup di foto e video</h3>
|
||||
<br/>
|
||||
<a href="https://immich.app">
|
||||
<img src="design/immich-screenshots.png" title="Main Screenshot">
|
||||
</a>
|
||||
<br/>
|
||||
<p align="center">
|
||||
<a href="README.md">English</a>
|
||||
<a href="README_ca_ES.md">Català</a>
|
||||
<a href="README_es_ES.md">Español</a>
|
||||
<a href="README_fr_FR.md">Français</a>
|
||||
<a href="README_ja_JP.md">日本語</a>
|
||||
<a href="README_ko_KR.md">한국어</a>
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
</p>
|
||||
|
||||
## Declino di responsabilità
|
||||
|
||||
- ⚠️ Il progetto è in fase di sviluppo **molto avanzato**.
|
||||
- ⚠️ Possibilità di bug e cambiamenti rilevanti.
|
||||
- ⚠️ **Non utilizzare l'app come unico salvataggio delle tue foto e dei tuoi video.**
|
||||
- ⚠️ Utilizza sempre una tecnica [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) di backup per le foto e i video a cui tieni!
|
||||
|
||||
## Contenuto
|
||||
|
||||
- [Documentazione Ufficiale](https://immich.app/docs)
|
||||
- [Roadmap](https://github.com/orgs/immich-app/projects/1)
|
||||
- [Demo](#demo)
|
||||
- [Funzionalità](#features)
|
||||
- [Introduzione](https://immich.app/docs/overview/introduction)
|
||||
- [Installazione](https://immich.app/docs/install/requirements)
|
||||
- [Linee Guida per Contribuire](https://immich.app/docs/overview/support-the-project)
|
||||
- [Supporta il Progetto](#support-the-project)
|
||||
|
||||
## Documentazione
|
||||
|
||||
La documentazione ufficiale, inclusa la guida all'installazione, è disponibile qui: https://immich.app/.
|
||||
|
||||
## Demo
|
||||
|
||||
Prova la demo del progetto https://demo.immich.app
|
||||
|
||||
Sull'app mobile, imposta `https://demo.immich.app/api` come `Server Endpoint URL`
|
||||
|
||||
```bash title="Demo Credential"
|
||||
Credenziali di accesso
|
||||
email: demo@immich.app
|
||||
password: demo
|
||||
```
|
||||
|
||||
```
|
||||
Spec: Free-tier Oracle VM - Amsterdam - 2.4Ghz quad-core ARM64 CPU, 24GB RAM
|
||||
```
|
||||
|
||||
# Funzionalità
|
||||
|
||||
| Funzionalità | Mobile | Web |
|
||||
| ---------------------------------------------- | ------ | --- |
|
||||
| Caricamento e visualizzazione di foto e video | Sì | Sì |
|
||||
| Backup automatico quando l'app è in esecuzione | Sì | N/A |
|
||||
| Selezione degli album per backup | Sì | N/A |
|
||||
| Download foto e video sul dispositivo | Sì | Sì |
|
||||
| Supporto multi utente | Sì | Sì |
|
||||
| Album e album condivisi | Sì | Sì |
|
||||
| Barra di scorrimento con trascinamento | Sì | Sì |
|
||||
| Supporto formati raw | Sì | Sì |
|
||||
| Visualizzazione metadata (EXIF, map) | Sì | Sì |
|
||||
| Ricerca per metadata, oggetti, volti e CLIP | Sì | Sì |
|
||||
| Funzioni di amministrazione degli utenti | No | Sì |
|
||||
| Backup in background | Sì | N/A |
|
||||
| Scroll virtuale | Sì | Sì |
|
||||
| Supporto OAuth | Sì | Sì |
|
||||
| API Keys | N/A | Sì |
|
||||
| Backup e riproduzione di LivePhoto | iOS | Sì |
|
||||
| Archiviazione impostata dall'utente | Sì | Sì |
|
||||
| Condivisione pubblica | No | Sì |
|
||||
| Archivio e Preferiti | Sì | Sì |
|
||||
| Mappa globale | Sì | Sì |
|
||||
| Collaborazione con utenti | Sì | Sì |
|
||||
| Riconoscimento facciale e categorizzazione | Sì | Sì |
|
||||
| Ricordi (x anni fa) | Sì | Sì |
|
||||
| Supporto offline | Sì | No |
|
||||
| Galleria sola lettura | Sì | Sì |
|
||||
|
||||
# Supporta il progetto
|
||||
|
||||
Mi dedico al progetto e non smetterò di farlo. Manterrò aggiornata la documentazione, aggiungerò nuove funzioni e risolverò i bug, ma non posso farlo da solo. Ho bisogno del tuo aiuto che mi da motivazione per continuare.
|
||||
|
||||
Come detto dal nostro host [selfhosted.show - Nell'episodio 'The-organization-must-not-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418), quello che il team ed io stiamo facendo è un lavoro enorme. Mi piacerebbe dedicarmi al progetto full-time e chiedo il tuo aiuto affinchè sia possibile.
|
||||
|
||||
Se pensi che Immich sia una buona causa e che l'app sia qualcosa che useresti nel lungo termine, sappi che puoi supportare il progetto scegliendo tra le opzioni sotto elencate.
|
||||
|
||||
## Donazioni
|
||||
|
||||
- [Donazione mensile](https://github.com/sponsors/alextran1502) tramite GitHub Sponsors
|
||||
- [Donazione una tantum](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) tramite GitHub Sponsors
|
||||
- [Librepay](https://liberapay.com/alex.tran1502/)
|
||||
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
|
||||
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
|
||||
- ZCash: u1smm4wvqegcp46zss2jf5xptchgeczp4rx7a0wu3mermf2wxahm26yyz5w9mw3f2p4emwlljxjumg774kgs8rntt9yags0whnzane4n67z4c7gppq4yyvcj404ne3r769prwzd9j8ntvqp44fa6d67sf7rmcfjmds3gmeceff4u8e92rh38nd30cr96xw6vfhk6scu4ws90ldzupr3sz
|
115
README_ja_JP.md
Normal file
115
README_ja_JP.md
Normal file
|
@ -0,0 +1,115 @@
|
|||
<p align="center">
|
||||
<br/>
|
||||
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-green.svg?color=3F51B5&style=for-the-badge&label=License&logoColor=000000&labelColor=ececec" alt="License: MIT"></a>
|
||||
<a href="https://discord.gg/D8JsnBEuKb">
|
||||
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" atl="Discord"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="design/immich-logo.svg" width="150" title="Login With Custom URL">
|
||||
</p>
|
||||
<h3 align="center">Immich - 高性能なセルフホスト 写真/ビデオバックアップソリューション</h3>
|
||||
<br/>
|
||||
<a href="https://immich.app">
|
||||
<img src="design/immich-screenshots.png" title="Main Screenshot">
|
||||
</a>
|
||||
<br/>
|
||||
<p align="center">
|
||||
<a href="README.md">English</a>
|
||||
<a href="README_ca_ES.md">Català</a>
|
||||
<a href="README_es_ES.md">Español</a>
|
||||
<a href="README_fr_FR.md">Français</a>
|
||||
<a href="README_it_IT.md">Italiano</a>
|
||||
<a href="README_ko_KR.md">한국어</a>
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
</p>
|
||||
|
||||
## 免責事項
|
||||
|
||||
- ⚠️ このプロジェクトは **非常に活発に** 開発中です。
|
||||
- ⚠️ バグの存在や変更が入ることも予想されます。
|
||||
- ⚠️ **写真やビデオを保存する唯一の方法としてこのアプリを使用しないでください。**
|
||||
- ⚠️ 大切な写真やビデオは、常に [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) のバックアッププランに従ってください!
|
||||
|
||||
## コンテンツ
|
||||
|
||||
- [公式ドキュメント](https://immich.app/docs)
|
||||
- [ロードマップ](https://github.com/orgs/immich-app/projects/1)
|
||||
- [デモ](#デモ)
|
||||
- [機能](#機能)
|
||||
- [紹介](https://immich.app/docs/overview/introduction)
|
||||
- [インストール](https://immich.app/docs/install/requirements)
|
||||
- [コントリビューションガイド](https://immich.app/docs/overview/support-the-project)
|
||||
- [プロジェクトのサポート](#プロジェクトのサポート)
|
||||
|
||||
## ドキュメント
|
||||
|
||||
インストールガイドを含む主なドキュメントは、https://immich.app/ です。
|
||||
|
||||
## デモ
|
||||
|
||||
web デモは https://demo.immich.app からアクセスできます
|
||||
|
||||
モバイルアプリの場合、`Server Endpoint URL` には `https://demo.immich.app/api` を使用することができます
|
||||
|
||||
```bash title="Demo Credential"
|
||||
The credential
|
||||
email: demo@immich.app
|
||||
password: demo
|
||||
```
|
||||
|
||||
```
|
||||
Spec: Free-tier Oracle VM - Amsterdam - 2.4Ghz quad-core ARM64 CPU, 24GB RAM
|
||||
```
|
||||
|
||||
# 機能
|
||||
|
||||
| 機能 | モバイル | Web |
|
||||
| ------------------------------------------- | ------ | --- |
|
||||
| ビデオや写真のアップロードと表示 | はい | はい |
|
||||
| アプリを開いたとき自動バックアップ | はい | N/A |
|
||||
| バックアップ用アルバム選択 | はい | N/A |
|
||||
| 写真やビデオをローカルデバイスにダウンロード | はい | はい |
|
||||
| マルチユーザー対応 | はい | はい |
|
||||
| アルバムと共有アルバム | はい | はい |
|
||||
| スクラブ可能/ドラッグ可能スクロールバ | はい | はい |
|
||||
| 生のフォーマットに対応 | はい | はい |
|
||||
| メタデータ表示(EXIF、地図) | はい | はい |
|
||||
| メタデータ、オブジェクト、フェース、CLIPによる検索 | はい | はい |
|
||||
| 管理機能(ユーザー管理) | いいえ | はい |
|
||||
| バックグラウンドバックアップ | はい | N/A |
|
||||
| 仮想スクロール | はい | はい |
|
||||
| OAuth サポート | はい | はい |
|
||||
| API キー | N/A | はい |
|
||||
| LivePhoto のバックアップと再生 | iOS | はい |
|
||||
| ユーザー定義のストレージ構造 | はい | はい |
|
||||
| 公開シェアリング | いいえ | はい |
|
||||
| アーカイブとお気に入り | はい | はい |
|
||||
| グローバルマップ | はい | はい |
|
||||
| パートナー共有 | はい | はい |
|
||||
| 思い出(x 年前)顔認識とクラスタリング | はい | はい |
|
||||
| 思い出(x 年前) | はい | はい |
|
||||
| オフラインサポート | はい | いいえ |
|
||||
| 読み取り専用ギャラリー | はい | はい |
|
||||
|
||||
# プロジェクトのサポート
|
||||
|
||||
私はこのプロジェクトにコミットしてきました。ドキュメントを更新し、新しい機能を追加し、バグを修正し続けるつもりですが、私ひとりではできません。だから、続けるためのモチベーションをさらに高めてくれる皆さんの助けが必要なのです。
|
||||
|
||||
[selfhosted.show - In the episode 'The-organization-must-いいえt-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418) のホストが言ったように、これはチームと私がやっていることの大規模な事業だ。そしていつの日か、フルタイムでこの仕事ができるようになりたいと思っています。
|
||||
|
||||
もし、あなたがこのプロジェクトに賛同し、このアプリを長く使い続けたいと思われるのであれば、以下のオプションから支援をご検討ください。
|
||||
|
||||
## 寄付
|
||||
|
||||
- GitHub スポンサー経由の[毎月の寄付](https://github.com/sponsors/alextran1502)
|
||||
- GitHub スポンサー経由の[一回寄付](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502)
|
||||
- [Librepay](https://liberapay.com/alex.tran1502/)
|
||||
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
|
||||
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
|
117
README_ko_KR.md
Normal file
117
README_ko_KR.md
Normal file
|
@ -0,0 +1,117 @@
|
|||
<p align="center">
|
||||
<br/>
|
||||
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-green.svg?color=3F51B5&style=for-the-badge&label=License&logoColor=000000&labelColor=ececec" alt="License: MIT"></a>
|
||||
<a href="https://discord.gg/D8JsnBEuKb">
|
||||
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" alt="Discord"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="design/immich-logo.svg" width="150" title="Login With Custom URL">
|
||||
</p>
|
||||
<h3 align="center">Immich - 고성능 자체 호스팅 사진 및 동영상 백업 솔루션</h3>
|
||||
<br/>
|
||||
<a href="https://immich.app">
|
||||
<img src="design/immich-screenshots.png" title="Main Screenshot">
|
||||
</a>
|
||||
<br/>
|
||||
<p align="center">
|
||||
<a href="README.md">English</a>
|
||||
<a href="README_ca_ES.md">Català</a>
|
||||
<a href="README_es_ES.md">Español</a>
|
||||
<a href="README_fr_FR.md">Français</a>
|
||||
<a href="README_it_IT.md">Italiano</a>
|
||||
<a href="README_ja_JP.md">日本語</a>
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
</p>
|
||||
|
||||
## 주의 사항
|
||||
|
||||
- ⚠️ 이 프로젝트는 **매우 활발히** 개발 중입니다.
|
||||
- ⚠️ 버그 및 잦은 변경 사항이 있을 수 있습니다.
|
||||
- ⚠️ **사진과 동영상을 저장하는 유일한 방법으로 사용하지 마세요.**
|
||||
- ⚠️ 중요한 사진과 동영상을 위해 항상 [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) 백업 계획을 따르세요!
|
||||
|
||||
## 목차
|
||||
|
||||
- [공식 문서](https://immich.app/docs)
|
||||
- [로드맵](https://github.com/orgs/immich-app/projects/1)
|
||||
- [데모](#demo)
|
||||
- [기능](#features)
|
||||
- [소개](https://immich.app/docs/overview/introduction)
|
||||
- [설치](https://immich.app/docs/install/requirements)
|
||||
- [기여 가이드](https://immich.app/docs/overview/support-the-project)
|
||||
- [프로젝트 지원](#support-the-project)
|
||||
|
||||
## 문서
|
||||
|
||||
설치 가이드를 포함한 주요 문서는 https://immich.app 에서 확인할 수 있습니다.
|
||||
|
||||
## 데모
|
||||
|
||||
https://demo.immich.app 에서 웹 데모를 체험할 수 있습니다.
|
||||
|
||||
모바일 앱의 경우 `서버 엔드포인트 URL`에 `https://demo.immich.app`를 입력합니다.
|
||||
|
||||
```bash title="Demo Credential"
|
||||
자격 증명
|
||||
email: demo@immich.app
|
||||
password: demo
|
||||
```
|
||||
|
||||
```
|
||||
사양: Free-tier Oracle VM - Amsterdam - 2.4Ghz quad-core ARM64 CPU, 24GB RAM
|
||||
```
|
||||
|
||||
## 기능
|
||||
|
||||
| 기능 | 모바일 | 웹 |
|
||||
| ------------------------------------ | ----- | ----- |
|
||||
| 사진, 동영상 업로드 및 보기 | 예 | 예 |
|
||||
| 앱을 열 때 자동으로 백업 | 예 | N/A |
|
||||
| 백업용 앨범 선택 | 예 | N/A |
|
||||
| 로컬 기기로 사진 및 동영상 다운로드 | 예 | 예 |
|
||||
| 다른 사용자 추가 | 예 | 예 |
|
||||
| 앨범 및 공유 앨범 | 예 | 예 |
|
||||
| 스와이프/드래그 가능한 스크롤 바 | 예 | 예 |
|
||||
| RAW 포맷 지원 | 예 | 예 |
|
||||
| 메타데이터 보기 (EXIF, 위치) | 예 | 예 |
|
||||
| 메타데이터, 사물, 얼굴 및 클립으로 검색 | 예 | 예 |
|
||||
| 관리 기능 (사용자 관리) | 아니요 | 예 |
|
||||
| 백그라운드 백업 | 예 | N/A |
|
||||
| 가상 스크롤 | 예 | 예 |
|
||||
| OAuth 지원 | 예 | 예 |
|
||||
| API 키 | N/A | 예 |
|
||||
| 라이브 포토/모션 포토 백업 및 재생 | 예 | 예 |
|
||||
| 사용자 정의 스토리지 구조 | 예 | 예 |
|
||||
| 모든 사용자와 공유 | 아니요 | 예 |
|
||||
| 아카이브 및 즐겨찾기 | 예 |예|
|
||||
| 글로벌 지도 | 예 | 예 |
|
||||
| 특정 사용자와 공유 | 예 | 예 |
|
||||
| 얼굴 인식 및 클러스터링 | 예 | 예 |
|
||||
| 추억 (~년 전) | 예 | 예 |
|
||||
| 오프라인 지원 | 예 | 아니요 |
|
||||
| 읽기 전용 갤러리 | 예 | 예 |
|
||||
| 사진 스택 | 예 | 예 |
|
||||
|
||||
## 프로젝트 지원
|
||||
|
||||
저는 이 프로젝트에 전념해왔고, 앞으로도 멈추지 않을 것입니다. 문서를 업데이트하고, 새로운 기능을 추가하고, 버그를 수정하려 합니다. 하지만 혼자서는 할 수 없습니다. 계속해서 나아갈 수 있는 추가적인 동기부여를 위해 당신의 도움이 필요합니다.
|
||||
|
||||
[selfhosted.show - In the episode 'The-organization-must-not-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418) 진행자가 말했듯이, 우리가 하고 있는 것은 대규모 프로젝트입니다. 언젠가는 이 일을 풀타임으로 하는 것을 희망하며, 이를 실현하기 위해 당신의 도움이 필요합니다.
|
||||
|
||||
만약 이에 동의하거나 이 앱을 장기간 사용하고자 한다면, 아래의 수단을 통해 이 프로젝트를 지원해 주세요.
|
||||
|
||||
### 후원
|
||||
|
||||
- GitHub 스폰서를 통한 [정기 후원](https://github.com/sponsors/alextran1502)
|
||||
- GitHub 스폰서를 통한 [일시 후원](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502)
|
||||
- [Librepay](https://liberapay.com/alex.tran1502/)
|
||||
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
|
||||
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
|
||||
- ZCash: u1smm4wvqegcp46zss2jf5xptchgeczp4rx7a0wu3mermf2wxahm26yyz5w9mw3f2p4emwlljxjumg774kgs8rntt9yags0whnzane4n67z4c7gppq4yyvcj404ne3r769prwzd9j8ntvqp44fa6d67sf7rmcfjmds3gmeceff4u8e92rh38nd30cr96xw6vfhk6scu4ws90ldzupr3sz
|
116
README_nl_NL.md
Normal file
116
README_nl_NL.md
Normal file
|
@ -0,0 +1,116 @@
|
|||
<p align="center">
|
||||
<br/>
|
||||
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-green.svg?color=3F51B5&style=for-the-badge&label=License&logoColor=000000&labelColor=ececec" alt="License: MIT"></a>
|
||||
<a href="https://discord.gg/D8JsnBEuKb">
|
||||
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" atl="Discord"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="design/immich-logo.svg" width="150" title="Login met aangepaste URL">
|
||||
</p>
|
||||
<h3 align="center">Immich - Hoogwaardige, self-hosted back-up oplossing voor foto's en video's</h3>
|
||||
<br/>
|
||||
<a href="https://immich.app">
|
||||
<img src="design/immich-screenshots.png" title="Main Screenshot">
|
||||
</a>
|
||||
<br/>
|
||||
<p align="center">
|
||||
<a href="README.md">English</a>
|
||||
<a href="README_ca_ES.md">Català</a>
|
||||
<a href="README_es_ES.md">Español</a>
|
||||
<a href="README_fr_FR.md">Français</a>
|
||||
<a href="README_it_IT.md">Italiano</a>
|
||||
<a href="README_ja_JP.md">日本語</a>
|
||||
<a href="README_ko_KR.md">한국어</a>
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
</p>
|
||||
|
||||
## Disclaimer
|
||||
|
||||
- ⚠️ Het project wordt momenteel **zeer actief** ontwikkeld.
|
||||
- ⚠️ Verwacht bugs en ingrijpende wijzigingen.
|
||||
- ⚠️ **Gebruik de app niet als de enige manier om uw foto's en video's op te slaan.**
|
||||
- ⚠️ Volg altijd het [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) backup plan voor je kostbare foto's en video's!
|
||||
|
||||
## Inhoud
|
||||
|
||||
- [Officiële documentatie](https://immich.app/docs)
|
||||
- [Roadmap](https://github.com/orgs/immich-app/projects/1)
|
||||
- [Demo](#demo)
|
||||
- [Functies](#functies)
|
||||
- [Introductie](https://immich.app/docs/overview/introduction)
|
||||
- [Installatie](https://immich.app/docs/install/requirements)
|
||||
- [Richtlijnen voor bijdragen](https://immich.app/docs/overview/support-the-project)
|
||||
- [Steun het project](#steun-het-project)
|
||||
|
||||
## Documentatie
|
||||
|
||||
De belangrijkste documentatie, inclusief installatie handleidingen, zijn te vinden op https://immich.app/.
|
||||
|
||||
## Demo
|
||||
|
||||
De demo is te bekijken op https://demo.immich.app.
|
||||
|
||||
Voor de mobiele app kunt u gebruik maken van `https://demo.immich.app/api` voor de `Server Endpoint URL`
|
||||
|
||||
```bash title="Demo Credential"
|
||||
De inloggegevens
|
||||
email: demo@immich.app
|
||||
wachtwoord: demo
|
||||
```
|
||||
|
||||
```
|
||||
Spec: Free-tier Oracle VM - Amsterdam - 2.4Ghz quad-core ARM64 CPU, 24GB RAM
|
||||
```
|
||||
|
||||
# Functies
|
||||
|
||||
| Functies | Mobiel | Web |
|
||||
|-----------------------------------------------------|--------|-----|
|
||||
| Upload en bekijk video's en foto's | Ja | Ja |
|
||||
| Automatische back-up wanneer de app wordt geopend | Ja | NVT |
|
||||
| Selectieve album(s) voor back-up | Ja | NVT |
|
||||
| Download foto's en video's naar een lokaal apparaat | Ja | Ja |
|
||||
| Ondersteuning voor meerdere gebruikers | Ja | Ja |
|
||||
| Album en gedeelde albums | Ja | Ja |
|
||||
| Versleepbare scroll balk | Ja | Ja |
|
||||
| Ondersteuning voor het RAW formaat | Ja | Ja |
|
||||
| Metagegevensweergave (EXIF, kaart) | Ja | Ja |
|
||||
| Zoek op metagegevens, objecten, gezichten en CLIP | Ja | Ja |
|
||||
| Administratieve functies (gebruikersbeheer) | Nee | Ja |
|
||||
| Back-up op de achtergrond | Ja | NVT |
|
||||
| Virtueel scrollen | Ja | Ja |
|
||||
| OAuth-ondersteuning | Ja | Ja |
|
||||
| API-sleutels | NVT | Ja |
|
||||
| LivePhoto-back-up en weergave | iOS | Ja |
|
||||
| Door de gebruiker gedefinieerde opslagstructuur | Ja | Ja |
|
||||
| Openbaar delen | Nee | Ja |
|
||||
| Archief en Favorieten | Ja | Ja |
|
||||
| Wereldkaart | Ja | Ja |
|
||||
| Delen met partner | Ja | Ja |
|
||||
| Gezichtsherkenning en groepering | Ja | Ja |
|
||||
| Herinneringen (x jaar geleden) | Ja | Ja |
|
||||
| Offline-ondersteuning | Ja | Nee |
|
||||
| Alleen-lezen galerij | Ja | Ja |
|
||||
|
||||
# Steun het project
|
||||
|
||||
Ik ben trouw aan dit project en ik zal niet stoppen. Ik zal de documenten blijven bijwerken, nieuwe functies toevoegen en bugs oplossen. Maar ik kan het niet alleen. Ik heb dus jouw hulp nodig om mij extra motivatie te geven om door te gaan.
|
||||
|
||||
Als onze gastheren in de [selfhosted.show - In de aflevering 'The-organization-must-Neet-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418) zeiden, dit is een eNeerme onderneming van wat het team en ik doen. En ik zou dit graag fulltime willen doen, ik vraag jouw hulp om dat mogelijk te maken.
|
||||
|
||||
Als je denkt dat dit het juiste doel is en de app iets is dat je jezelf al heel lang ziet gebruiken, overweeg dan om het project te steunen met de onderstaande optie.
|
||||
|
||||
## Doneren
|
||||
|
||||
- [Maandelijkse donatie](https://github.com/sponsors/alextran1502) via GitHub Sponsors
|
||||
- [Eenmalige donatie](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) via GitHub Sponsors
|
||||
- [Librepay](https://liberapay.com/alex.tran1502/)
|
||||
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
|
||||
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
|
||||
- ZCash: u1smm4wvqegcp46zss2jf5xptchgeczp4rx7a0wu3mermf2wxahm26yyz5w9mw3f2p4emwlljxjumg774kgs8rntt9yags0whnzane4n67z4c7gppq4yyvcj404ne3r769prwzd9j8ntvqp44fa6d67sf7rmcfjmds3gmeceff4u8e92rh38nd30cr96xw6vfhk6scu4ws90ldzupr3sz
|
113
README_tr_TR.md
Normal file
113
README_tr_TR.md
Normal file
|
@ -0,0 +1,113 @@
|
|||
<p align="center">
|
||||
<br/>
|
||||
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-green.svg?color=3F51B5&style=for-the-badge&label=License&logoColor=000000&labelColor=ececec" alt="License: MIT"></a>
|
||||
<a href="https://discord.gg/D8JsnBEuKb">
|
||||
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" atl="Discord"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="design/immich-logo.svg" width="150" title="Login With Custom URL">
|
||||
</p>
|
||||
<h3 align="center">Immich - Yüksek performanslı, kendine ait barındırılan fotoğraf ve video yedekleme çözümü</h3>
|
||||
<br/>
|
||||
<a href="https://immich.app">
|
||||
<img src="design/immich-screenshots.png" title="Main Screenshot">
|
||||
</a>
|
||||
<br/>
|
||||
<p align="center">
|
||||
<a href="README.md">English</a>
|
||||
<a href="README_ca_ES.md">Català</a>
|
||||
<a href="README_es_ES.md">Español</a>
|
||||
<a href="README_fr_FR.md">Français</a>
|
||||
<a href="README_it_IT.md">Italiano</a>
|
||||
<a href="README_ja_JP.md">日本語</a>
|
||||
<a href="README_ko_KR.md">한국어</a>
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_zh_CN.md">中文</a>
|
||||
</p>
|
||||
|
||||
## Feragatname
|
||||
|
||||
- ⚠️ Proje **çok aktif** bir şekilde geliştirilmektedir.
|
||||
- ⚠️ Hatalar ve uygulama yapısını bozan değişiklikler olabilir.
|
||||
- ⚠️ **Uygulamayı, fotoğraflarınızı ve videolarınızı saklamanın tek yöntemi olarak kullanmayın!**
|
||||
|
||||
## Content
|
||||
|
||||
- [Resmi Belgeler](https://immich.app/docs)
|
||||
- [Yol Haritası](https://github.com/orgs/immich-app/projects/1)
|
||||
- [Demo](#demo)
|
||||
- [Özellikler](#özellikler)
|
||||
- [Giriş](https://immich.app/docs/overview/introduction)
|
||||
- [Kurulum](https://immich.app/docs/install/requirements)
|
||||
- [Katkı Sağlama Rehberi](https://immich.app/docs/overview/support-the-project)
|
||||
- [Projeyi Destekle](#projeyi-destekle)
|
||||
|
||||
## Belgeler
|
||||
|
||||
Kurulum dahil olmak üzere resmi belgeleri https://immich.app/ adresinde bulabilirsiniz.
|
||||
|
||||
## Demo
|
||||
|
||||
Web demo adresi: https://demo.immich.app
|
||||
|
||||
Mobil uygulama için `Server Endpoint URL` olarak `https://demo.immich.app/api` adresini kullanabilirsiniz.
|
||||
|
||||
```bash title="Demo Bilgileri"
|
||||
Giriş bilgileri:
|
||||
email: demo@immich.app
|
||||
password: demo
|
||||
```
|
||||
|
||||
```
|
||||
Server Özellikleri: Free-tier Oracle VM - Amsterdam - 2.4Ghz quad-core ARM64 CPU, 24GB RAM
|
||||
```
|
||||
|
||||
# Özellikler
|
||||
|
||||
| Özellikler | Mobile | Web |
|
||||
| ----------------------------------------------------| ------ | --- |
|
||||
| Videoları ve fotoğrafları yükleme ve görüntüleme | Evet | Evet |
|
||||
| Uygulama açıldığında otomatik yedekleme | Evet | N/A |
|
||||
| Yedekleme için seçilebilir albüm(ler) | Evet | N/A |
|
||||
| Fotoğrafları ve videoları yerel cihaza yükleme | Evet | Evet |
|
||||
| Çoklu kullanıcı desteği | Evet | Evet |
|
||||
| Albüm ve paylaşılan albümler | Evet | Evet |
|
||||
| Silinebilir/sürüklenebilir kaydırma çubuğu | Evet | Evet |
|
||||
| RAW (HEIC, HEIF, DNG, Apple ProRaw) format desteği | Evet | Evet |
|
||||
| Metadata'ya uygun görüntüleme (EXIF, map) | Evet | Evet |
|
||||
| Metadata, objects, faces ve CLIP'e göre arama | Evet | Evet |
|
||||
| Yönetimsel işlevler (kullanıcı yönetimi) | Hayır | Evet |
|
||||
| Arka planda yedekleme | Evet | N/A |
|
||||
| Sanal kaydırma | Evet | Evet |
|
||||
| OAuth desteği | Evet | Evet |
|
||||
| API anahtarları | N/A | Evet |
|
||||
| LivePhoto yedekleme ve oynatma | iOS | Evet |
|
||||
| Kullanıcı tanımlı depolama yapısı | Evet | Evet |
|
||||
| Herkese açık paylaşım | Hayır | Evet |
|
||||
| Arşiv ve Favoriler | Evet | Evet |
|
||||
| Dünya haritası | Hayır | Evet |
|
||||
| Partner paylaşımı | Evet | Evet |
|
||||
| Yüz tanıma ve kümeleme | Hayır | Evet |
|
||||
| Çevrimdışı destek | Evet | Hayır|
|
||||
|
||||
# Projeyi Destekle
|
||||
|
||||
Bu projeye bağlı kaldım ve durmayacağım. Belgeleri güncellemeye, yeni özellikler eklemeye ve hataları düzeltmeye devam edeceğim. Ancak bunu tek başıma yapamam. Bu yüzden devam etme konusunda bana motivasyon sağlamanız için yardımınıza ihtiyacım var.
|
||||
|
||||
[selfhosted.show - In the episode 'The-organization-must-not-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418) bölümünde söylendiği üzere,bu projede takımımın ve benim projeye harcadağımız büyük bir çaba var. Bir gün bunu tam zamanlı olarak yapabilmeyi çok isterim. Bunu gerçekleştirebilmek için gerçekten sizlerin desteğine ihtiyacım var.
|
||||
|
||||
Eğer bu size doğru bir amaç gibi geliyorsa ve uygulamanın uzun bir süre boyunca kullanacağınız bir şey olduğunu düşünüyorsanız, aşağıdaki bağlantılardan birini kullanarak bana destek olabilirsiniz.
|
||||
|
||||
## Bağış
|
||||
|
||||
- [Aylık bağış](https://github.com/sponsors/alextran1502) via GitHub Sponsors
|
||||
- [Bir seferlik bağış](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) via GitHub Sponsors
|
||||
- [Librepay](https://liberapay.com/alex.tran1502/)
|
||||
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
|
||||
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
|
||||
- ZCash: u1smm4wvqegcp46zss2jf5xptchgeczp4rx7a0wu3mermf2wxahm26yyz5w9mw3f2p4emwlljxjumg774kgs8rntt9yags0whnzane4n67z4c7gppq4yyvcj404ne3r769prwzd9j8ntvqp44fa6d67sf7rmcfjmds3gmeceff4u8e92rh38nd30cr96xw6vfhk6scu4ws90ldzupr3sz
|
120
README_zh_CN.md
Normal file
120
README_zh_CN.md
Normal file
|
@ -0,0 +1,120 @@
|
|||
<p align="center">
|
||||
<br/>
|
||||
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-green.svg?color=3F51B5&style=for-the-badge&label=License&logoColor=000000&labelColor=ececec" alt="License: MIT"></a>
|
||||
<a href="https://discord.gg/D8JsnBEuKb">
|
||||
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" atl="Discord"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="design/immich-logo.svg" width="150" title="Login With Custom URL">
|
||||
</p>
|
||||
<h3 align="center">Immich - 高性能的自托管照片和视频备份方案</h3>
|
||||
<p align="center">
|
||||
请注意: 此 README 不是由 Immich 团队维护, 而是依靠贡献者来更新的,这意味着它可能并不会被及时更新。感谢理解。
|
||||
</p>
|
||||
<br/>
|
||||
<a href="https://immich.app">
|
||||
<img src="design/immich-screenshots.png" title="Main Screenshot">
|
||||
</a>
|
||||
<br/>
|
||||
|
||||
<p align="center">
|
||||
<a href="README.md">English</a>
|
||||
<a href="README_ca_ES.md">Català</a>
|
||||
<a href="README_es_ES.md">Español</a>
|
||||
<a href="README_fr_FR.md">Français</a>
|
||||
<a href="README_it_IT.md">Italiano</a>
|
||||
<a href="README_ja_JP.md">日本語</a>
|
||||
<a href="README_ko_KR.md">한국어</a>
|
||||
<a href="README_de_DE.md">Deutsch</a>
|
||||
<a href="README_nl_NL.md">Nederlands</a>
|
||||
<a href="README_tr_TR.md">Türkçe</a>
|
||||
</p>
|
||||
|
||||
## 免责声明
|
||||
|
||||
- ⚠️ 本项目正在 **非常活跃** 地开发中。
|
||||
- ⚠️ 可能存在 bug 或者随时有重大变更。
|
||||
- ⚠️ **不要把本软件作为您存储照片或视频的唯一方式。**
|
||||
- ⚠️ 为了您宝贵的照片与视频,始终遵守 [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) 备份方案!
|
||||
|
||||
## 目录
|
||||
|
||||
- [官方文档](https://immich.app/docs)
|
||||
- [路线图](https://github.com/orgs/immich-app/projects/1)
|
||||
- [示例](#示例)
|
||||
- [功能特性](#功能特性)
|
||||
- [介绍](https://immich.app/docs/overview/introduction)
|
||||
- [安装](https://immich.app/docs/install/requirements)
|
||||
- [贡献指南](https://immich.app/docs/overview/support-the-project)
|
||||
- [支持本项目](#支持本项目)
|
||||
|
||||
## 官方文档
|
||||
|
||||
您可以在 https://immich.app/ 找到官方文档(包含安装手册)。
|
||||
|
||||
## 示例
|
||||
|
||||
您可以在 https://demo.immich.app 访问示例。
|
||||
|
||||
在移动端, 您可以使用 `https://demo.immich.app/api` 获取 `服务终端链接`
|
||||
|
||||
```bash title="示例认证信息"
|
||||
认证信息
|
||||
邮箱: demo@immich.app
|
||||
密码: demo
|
||||
```
|
||||
|
||||
```
|
||||
规格: 甲骨文免费虚拟机套餐——阿姆斯特丹 4核 2.4Ghz ARM64 CPU, 24GB RAM。
|
||||
```
|
||||
|
||||
# 功能特性
|
||||
|
||||
| 功能特性 | 移动端 | 网页端 |
|
||||
| ------------------------------------------- | ------- | --- |
|
||||
| 上传并查看照片和视频 | 是 | 是 |
|
||||
| 软件运行时自动备份 | 是 | N/A |
|
||||
| 选择需要备份的相册 | 是 | N/A |
|
||||
| 下载照片和视频到本地 | 是 | 是 |
|
||||
| 多用户支持 | 是 | 是 |
|
||||
| 相册 | 是 | 是 |
|
||||
| 共享相册 | 是 | 是 |
|
||||
| 可拖动的快速导航栏 | 是 | 是 |
|
||||
| 支持RAW格式 (HEIC, HEIF, DNG, Apple ProRaw) | 是 | 是 |
|
||||
| 元数据视图(EXIF, 地图) | 是 | 是 |
|
||||
| 通过元数据、对象和标签进行搜索 | 是 | 是 |
|
||||
| 管理功能(用户管理) | 否 | 是 |
|
||||
| 后台备份 | 是 | N/A |
|
||||
| 虚拟滚动 | 是 | 是 |
|
||||
| OAuth 支持 | 是 | 是 |
|
||||
| API Keys|N/A|是|
|
||||
| 实况照片备份和查看 | 仅 iOS | 是 |
|
||||
|用户自定义存储结构|是|是|
|
||||
|公共分享|否|是|
|
||||
|归档与收藏功能|是|是|
|
||||
|全局地图|否|是|
|
||||
|好友分享|是|是|
|
||||
|人像识别与分组|是|是|
|
||||
|回忆(那年今日)|是|是|
|
||||
|离线支持|是|否|
|
||||
|只读相册|是|是|
|
||||
|
||||
# 支持本项目
|
||||
|
||||
我已经致力于本项目并且将我会持续更新文档、新增功能和修复问题。但是独木不成林,我需要您给予我坚持下去的动力。
|
||||
|
||||
就像我在 [selfhosted.show - In the episode 'The-organization-must-not-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418) 节目里说的一样,这是我和团队的一项艰巨任务。并且我希望某一天我能够全职开发本项目,在此我请求您能够助我梦想成真。
|
||||
|
||||
如果您使用了本项目一段时间,并且觉得上面的话有道理,那么请您考虑通过下列任一方式支持我吧。
|
||||
|
||||
## 捐赠
|
||||
|
||||
- 通过 GitHub Sponsors [按月捐赠](https://github.com/sponsors/alextran1502)
|
||||
- 通过 Github Sponsors [单次捐赠](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502)
|
||||
- [Librepay](https://liberapay.com/alex.tran1502/)
|
||||
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
|
||||
- 比特币: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
|
20
cli/.editorconfig
Normal file
20
cli/.editorconfig
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{ts,js}]
|
||||
quote_type = single
|
||||
|
||||
[*.{md,mdx}]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{yml,yaml}]
|
||||
quote_type = double
|
1
cli/.eslintignore
Normal file
1
cli/.eslintignore
Normal file
|
@ -0,0 +1 @@
|
|||
/dist
|
24
cli/.eslintrc.js
Normal file
24
cli/.eslintrc.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
sourceType: 'module',
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js'],
|
||||
rules: {
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
'prettier/prettier': 0,
|
||||
},
|
||||
};
|
13
cli/.gitignore
vendored
Normal file
13
cli/.gitignore
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
*-debug.log
|
||||
*-error.log
|
||||
/.nyc_output
|
||||
/dist
|
||||
/lib
|
||||
/tmp
|
||||
/yarn.lock
|
||||
node_modules
|
||||
oclif.manifest.json
|
||||
|
||||
.vscode
|
||||
.idea
|
||||
/coverage/
|
10
cli/.npmignore
Normal file
10
cli/.npmignore
Normal file
|
@ -0,0 +1,10 @@
|
|||
**/*.spec.js
|
||||
.editorconfig
|
||||
.eslintignore
|
||||
.eslintrc.js
|
||||
.prettierignore
|
||||
.prettierrc
|
||||
package-lock.json
|
||||
testSetup.js
|
||||
tsconfig.json
|
||||
tsconfig.build.json
|
18
cli/.prettierignore
Normal file
18
cli/.prettierignore
Normal file
|
@ -0,0 +1,18 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
src/api/open-api
|
||||
*.md
|
||||
*.json
|
||||
coverage
|
||||
dist
|
||||
**/migrations/**
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
6
cli/.prettierrc
Normal file
6
cli/.prettierrc
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 120,
|
||||
"semi": true
|
||||
}
|
21
cli/LICENSE
Normal file
21
cli/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 Hau Tran
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
19
cli/README.md
Normal file
19
cli/README.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
A command-line interface for interfacing with the self-hosted photo manager [Immich](https://immich.app/).
|
||||
|
||||
Please see the [Immich CLI documentation](https://immich.app/docs/features/command-line-interface).
|
||||
|
||||
# For developers
|
||||
|
||||
To run the Immich CLI from source, run the following in the cli folder:
|
||||
|
||||
$ npm run build
|
||||
$ ts-node .
|
||||
|
||||
You'll need ts-node, the easiest way to install it is to use npm:
|
||||
|
||||
$ npm i -g ts-node
|
||||
|
||||
You can also build and install the CLI using
|
||||
|
||||
$ npm run build
|
||||
$ npm install -g .
|
11610
cli/package-lock.json
generated
Normal file
11610
cli/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
87
cli/package.json
Normal file
87
cli/package.json
Normal file
|
@ -0,0 +1,87 @@
|
|||
{
|
||||
"name": "@immich/cli",
|
||||
"version": "2.0.4",
|
||||
"description": "Command Line Interface (CLI) for Immich",
|
||||
"main": "dist/index.js",
|
||||
"bin": {
|
||||
"immich": "./dist/src/index.js"
|
||||
},
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"immich",
|
||||
"cli"
|
||||
],
|
||||
"dependencies": {
|
||||
"axios": "^1.6.2",
|
||||
"byte-size": "^8.1.1",
|
||||
"cli-progress": "^3.12.0",
|
||||
"commander": "^11.0.0",
|
||||
"form-data": "^4.0.0",
|
||||
"glob": "^10.3.1",
|
||||
"yaml": "^2.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/byte-size": "^8.1.0",
|
||||
"@types/chai": "^4.3.5",
|
||||
"@types/cli-progress": "^3.11.0",
|
||||
"@types/jest": "^29.5.2",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/mime-types": "^2.1.1",
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/node": "^20.3.1",
|
||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||
"@typescript-eslint/parser": "^6.0.0",
|
||||
"chai": "^4.3.7",
|
||||
"eslint": "^8.43.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-jest": "^27.2.2",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-unicorn": "^49.0.0",
|
||||
"jest": "^29.5.0",
|
||||
"jest-extended": "^4.0.0",
|
||||
"jest-message-util": "^29.5.0",
|
||||
"jest-mock-axios": "^4.7.2",
|
||||
"jest-when": "^3.5.2",
|
||||
"mock-fs": "^5.2.0",
|
||||
"ts-jest": "^29.1.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"tslib": "^2.5.3",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc --project tsconfig.build.json",
|
||||
"lint": "eslint \"src/**/*.ts\" --max-warnings 0",
|
||||
"prepack": "npm run build",
|
||||
"test": "jest",
|
||||
"test:cov": "jest --coverage",
|
||||
"format": "prettier --check .",
|
||||
"format:fix": "prettier --write .",
|
||||
"check": "tsc --noEmit"
|
||||
},
|
||||
"jest": {
|
||||
"clearMocks": true,
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"ts"
|
||||
],
|
||||
"rootDir": ".",
|
||||
"testRegex": ".*\\.spec\\.ts$",
|
||||
"transform": {
|
||||
"^.+\\.ts$": "ts-jest"
|
||||
},
|
||||
"collectCoverageFrom": [
|
||||
"<rootDir>/src/**/*.(t|j)s"
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"^@api(|/.*)$": "<rootDir>/src/api/$1"
|
||||
},
|
||||
"coverageDirectory": "./coverage",
|
||||
"testEnvironment": "node"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "github:immich-app/immich",
|
||||
"directory": "cli"
|
||||
}
|
||||
}
|
52
cli/src/api/client.ts
Normal file
52
cli/src/api/client.ts
Normal file
|
@ -0,0 +1,52 @@
|
|||
import {
|
||||
AlbumApi,
|
||||
APIKeyApi,
|
||||
AssetApi,
|
||||
AuthenticationApi,
|
||||
Configuration,
|
||||
JobApi,
|
||||
OAuthApi,
|
||||
ServerInfoApi,
|
||||
SystemConfigApi,
|
||||
UserApi,
|
||||
} from './open-api';
|
||||
import { ApiConfiguration } from '../cores/api-configuration';
|
||||
import FormData from 'form-data';
|
||||
|
||||
export class ImmichApi {
|
||||
public userApi: UserApi;
|
||||
public albumApi: AlbumApi;
|
||||
public assetApi: AssetApi;
|
||||
public authenticationApi: AuthenticationApi;
|
||||
public oauthApi: OAuthApi;
|
||||
public serverInfoApi: ServerInfoApi;
|
||||
public jobApi: JobApi;
|
||||
public keyApi: APIKeyApi;
|
||||
public systemConfigApi: SystemConfigApi;
|
||||
|
||||
private readonly config;
|
||||
public readonly apiConfiguration: ApiConfiguration;
|
||||
|
||||
constructor(instanceUrl: string, apiKey: string) {
|
||||
this.apiConfiguration = new ApiConfiguration(instanceUrl, apiKey);
|
||||
this.config = new Configuration({
|
||||
basePath: instanceUrl,
|
||||
baseOptions: {
|
||||
headers: {
|
||||
'x-api-key': apiKey,
|
||||
},
|
||||
},
|
||||
formDataCtor: FormData,
|
||||
});
|
||||
|
||||
this.userApi = new UserApi(this.config);
|
||||
this.albumApi = new AlbumApi(this.config);
|
||||
this.assetApi = new AssetApi(this.config);
|
||||
this.authenticationApi = new AuthenticationApi(this.config);
|
||||
this.oauthApi = new OAuthApi(this.config);
|
||||
this.serverInfoApi = new ServerInfoApi(this.config);
|
||||
this.jobApi = new JobApi(this.config);
|
||||
this.keyApi = new APIKeyApi(this.config);
|
||||
this.systemConfigApi = new SystemConfigApi(this.config);
|
||||
}
|
||||
}
|
4
cli/src/api/open-api/.gitignore
vendored
Normal file
4
cli/src/api/open-api/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
wwwroot/*.js
|
||||
node_modules
|
||||
typings
|
||||
dist
|
1
cli/src/api/open-api/.npmignore
Normal file
1
cli/src/api/open-api/.npmignore
Normal file
|
@ -0,0 +1 @@
|
|||
# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm
|
23
cli/src/api/open-api/.openapi-generator-ignore
Normal file
23
cli/src/api/open-api/.openapi-generator-ignore
Normal file
|
@ -0,0 +1,23 @@
|
|||
# OpenAPI Generator Ignore
|
||||
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
|
||||
|
||||
# Use this file to prevent files from being overwritten by the generator.
|
||||
# The patterns follow closely to .gitignore or .dockerignore.
|
||||
|
||||
# As an example, the C# client generator defines ApiClient.cs.
|
||||
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
|
||||
#ApiClient.cs
|
||||
|
||||
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
|
||||
#foo/*/qux
|
||||
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
|
||||
|
||||
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
|
||||
#foo/**/qux
|
||||
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
|
||||
|
||||
# You can also negate patterns with an exclamation (!).
|
||||
# For example, you can ignore all files in a docs folder with the file extension .md:
|
||||
#docs/*.md
|
||||
# Then explicitly reverse the ignore rule for a single file:
|
||||
#!docs/README.md
|
9
cli/src/api/open-api/.openapi-generator/FILES
Normal file
9
cli/src/api/open-api/.openapi-generator/FILES
Normal file
|
@ -0,0 +1,9 @@
|
|||
.gitignore
|
||||
.npmignore
|
||||
.openapi-generator-ignore
|
||||
api.ts
|
||||
base.ts
|
||||
common.ts
|
||||
configuration.ts
|
||||
git_push.sh
|
||||
index.ts
|
1
cli/src/api/open-api/.openapi-generator/VERSION
Normal file
1
cli/src/api/open-api/.openapi-generator/VERSION
Normal file
|
@ -0,0 +1 @@
|
|||
6.5.0
|
18339
cli/src/api/open-api/api.ts
generated
Normal file
18339
cli/src/api/open-api/api.ts
generated
Normal file
File diff suppressed because it is too large
Load diff
72
cli/src/api/open-api/base.ts
generated
Normal file
72
cli/src/api/open-api/base.ts
generated
Normal file
|
@ -0,0 +1,72 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Immich
|
||||
* Immich API
|
||||
*
|
||||
* The version of the OpenAPI document: 1.89.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
import type { Configuration } from './configuration';
|
||||
// Some imports not used depending on template conditions
|
||||
// @ts-ignore
|
||||
import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios';
|
||||
import globalAxios from 'axios';
|
||||
|
||||
export const BASE_PATH = "/api".replace(/\/+$/, "");
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const COLLECTION_FORMATS = {
|
||||
csv: ",",
|
||||
ssv: " ",
|
||||
tsv: "\t",
|
||||
pipes: "|",
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface RequestArgs
|
||||
*/
|
||||
export interface RequestArgs {
|
||||
url: string;
|
||||
options: AxiosRequestConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @class BaseAPI
|
||||
*/
|
||||
export class BaseAPI {
|
||||
protected configuration: Configuration | undefined;
|
||||
|
||||
constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) {
|
||||
if (configuration) {
|
||||
this.configuration = configuration;
|
||||
this.basePath = configuration.basePath || this.basePath;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @class RequiredError
|
||||
* @extends {Error}
|
||||
*/
|
||||
export class RequiredError extends Error {
|
||||
constructor(public field: string, msg?: string) {
|
||||
super(msg);
|
||||
this.name = "RequiredError"
|
||||
}
|
||||
}
|
150
cli/src/api/open-api/common.ts
generated
Normal file
150
cli/src/api/open-api/common.ts
generated
Normal file
|
@ -0,0 +1,150 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Immich
|
||||
* Immich API
|
||||
*
|
||||
* The version of the OpenAPI document: 1.89.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
import type { Configuration } from "./configuration";
|
||||
import type { RequestArgs } from "./base";
|
||||
import type { AxiosInstance, AxiosResponse } from 'axios';
|
||||
import { RequiredError } from "./base";
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const DUMMY_BASE_URL = 'https://example.com'
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws {RequiredError}
|
||||
* @export
|
||||
*/
|
||||
export const assertParamExists = function (functionName: string, paramName: string, paramValue: unknown) {
|
||||
if (paramValue === null || paramValue === undefined) {
|
||||
throw new RequiredError(paramName, `Required parameter ${paramName} was null or undefined when calling ${functionName}.`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const setApiKeyToObject = async function (object: any, keyParamName: string, configuration?: Configuration) {
|
||||
if (configuration && configuration.apiKey) {
|
||||
const localVarApiKeyValue = typeof configuration.apiKey === 'function'
|
||||
? await configuration.apiKey(keyParamName)
|
||||
: await configuration.apiKey;
|
||||
object[keyParamName] = localVarApiKeyValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const setBasicAuthToObject = function (object: any, configuration?: Configuration) {
|
||||
if (configuration && (configuration.username || configuration.password)) {
|
||||
object["auth"] = { username: configuration.username, password: configuration.password };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const setBearerAuthToObject = async function (object: any, configuration?: Configuration) {
|
||||
if (configuration && configuration.accessToken) {
|
||||
const accessToken = typeof configuration.accessToken === 'function'
|
||||
? await configuration.accessToken()
|
||||
: await configuration.accessToken;
|
||||
object["Authorization"] = "Bearer " + accessToken;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const setOAuthToObject = async function (object: any, name: string, scopes: string[], configuration?: Configuration) {
|
||||
if (configuration && configuration.accessToken) {
|
||||
const localVarAccessTokenValue = typeof configuration.accessToken === 'function'
|
||||
? await configuration.accessToken(name, scopes)
|
||||
: await configuration.accessToken;
|
||||
object["Authorization"] = "Bearer " + localVarAccessTokenValue;
|
||||
}
|
||||
}
|
||||
|
||||
function setFlattenedQueryParams(urlSearchParams: URLSearchParams, parameter: any, key: string = ""): void {
|
||||
if (parameter == null) return;
|
||||
if (typeof parameter === "object") {
|
||||
if (Array.isArray(parameter)) {
|
||||
(parameter as any[]).forEach(item => setFlattenedQueryParams(urlSearchParams, item, key));
|
||||
}
|
||||
else {
|
||||
Object.keys(parameter).forEach(currentKey =>
|
||||
setFlattenedQueryParams(urlSearchParams, parameter[currentKey], `${key}${key !== '' ? '.' : ''}${currentKey}`)
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (urlSearchParams.has(key)) {
|
||||
urlSearchParams.append(key, parameter);
|
||||
}
|
||||
else {
|
||||
urlSearchParams.set(key, parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const setSearchParams = function (url: URL, ...objects: any[]) {
|
||||
const searchParams = new URLSearchParams(url.search);
|
||||
setFlattenedQueryParams(searchParams, objects);
|
||||
url.search = searchParams.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const serializeDataIfNeeded = function (value: any, requestOptions: any, configuration?: Configuration) {
|
||||
const nonString = typeof value !== 'string';
|
||||
const needsSerialization = nonString && configuration && configuration.isJsonMime
|
||||
? configuration.isJsonMime(requestOptions.headers['Content-Type'])
|
||||
: nonString;
|
||||
return needsSerialization
|
||||
? JSON.stringify(value !== undefined ? value : {})
|
||||
: (value || "");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const toPathString = function (url: URL) {
|
||||
return url.pathname + url.search + url.hash
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const createRequestFunction = function (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) {
|
||||
return <T = unknown, R = AxiosResponse<T>>(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
|
||||
const axiosRequestArgs = {...axiosArgs.options, url: (configuration?.basePath || basePath) + axiosArgs.url};
|
||||
return axios.request<T, R>(axiosRequestArgs);
|
||||
};
|
||||
}
|
101
cli/src/api/open-api/configuration.ts
generated
Normal file
101
cli/src/api/open-api/configuration.ts
generated
Normal file
|
@ -0,0 +1,101 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Immich
|
||||
* Immich API
|
||||
*
|
||||
* The version of the OpenAPI document: 1.89.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
export interface ConfigurationParameters {
|
||||
apiKey?: string | Promise<string> | ((name: string) => string) | ((name: string) => Promise<string>);
|
||||
username?: string;
|
||||
password?: string;
|
||||
accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise<string>);
|
||||
basePath?: string;
|
||||
baseOptions?: any;
|
||||
formDataCtor?: new () => any;
|
||||
}
|
||||
|
||||
export class Configuration {
|
||||
/**
|
||||
* parameter for apiKey security
|
||||
* @param name security name
|
||||
* @memberof Configuration
|
||||
*/
|
||||
apiKey?: string | Promise<string> | ((name: string) => string) | ((name: string) => Promise<string>);
|
||||
/**
|
||||
* parameter for basic security
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Configuration
|
||||
*/
|
||||
username?: string;
|
||||
/**
|
||||
* parameter for basic security
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Configuration
|
||||
*/
|
||||
password?: string;
|
||||
/**
|
||||
* parameter for oauth2 security
|
||||
* @param name security name
|
||||
* @param scopes oauth2 scope
|
||||
* @memberof Configuration
|
||||
*/
|
||||
accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise<string>);
|
||||
/**
|
||||
* override base path
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Configuration
|
||||
*/
|
||||
basePath?: string;
|
||||
/**
|
||||
* base options for axios calls
|
||||
*
|
||||
* @type {any}
|
||||
* @memberof Configuration
|
||||
*/
|
||||
baseOptions?: any;
|
||||
/**
|
||||
* The FormData constructor that will be used to create multipart form data
|
||||
* requests. You can inject this here so that execution environments that
|
||||
* do not support the FormData class can still run the generated client.
|
||||
*
|
||||
* @type {new () => FormData}
|
||||
*/
|
||||
formDataCtor?: new () => any;
|
||||
|
||||
constructor(param: ConfigurationParameters = {}) {
|
||||
this.apiKey = param.apiKey;
|
||||
this.username = param.username;
|
||||
this.password = param.password;
|
||||
this.accessToken = param.accessToken;
|
||||
this.basePath = param.basePath;
|
||||
this.baseOptions = param.baseOptions;
|
||||
this.formDataCtor = param.formDataCtor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given MIME is a JSON MIME.
|
||||
* JSON MIME examples:
|
||||
* application/json
|
||||
* application/json; charset=UTF8
|
||||
* APPLICATION/JSON
|
||||
* application/vnd.company+json
|
||||
* @param mime - MIME (Multipurpose Internet Mail Extensions)
|
||||
* @return True if the given MIME is JSON, false otherwise.
|
||||
*/
|
||||
public isJsonMime(mime: string): boolean {
|
||||
const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i');
|
||||
return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json');
|
||||
}
|
||||
}
|
57
cli/src/api/open-api/git_push.sh
Normal file
57
cli/src/api/open-api/git_push.sh
Normal file
|
@ -0,0 +1,57 @@
|
|||
#!/bin/sh
|
||||
# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/
|
||||
#
|
||||
# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com"
|
||||
|
||||
git_user_id=$1
|
||||
git_repo_id=$2
|
||||
release_note=$3
|
||||
git_host=$4
|
||||
|
||||
if [ "$git_host" = "" ]; then
|
||||
git_host="github.com"
|
||||
echo "[INFO] No command line input provided. Set \$git_host to $git_host"
|
||||
fi
|
||||
|
||||
if [ "$git_user_id" = "" ]; then
|
||||
git_user_id="GIT_USER_ID"
|
||||
echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id"
|
||||
fi
|
||||
|
||||
if [ "$git_repo_id" = "" ]; then
|
||||
git_repo_id="GIT_REPO_ID"
|
||||
echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id"
|
||||
fi
|
||||
|
||||
if [ "$release_note" = "" ]; then
|
||||
release_note="Minor update"
|
||||
echo "[INFO] No command line input provided. Set \$release_note to $release_note"
|
||||
fi
|
||||
|
||||
# Initialize the local directory as a Git repository
|
||||
git init
|
||||
|
||||
# Adds the files in the local repository and stages them for commit.
|
||||
git add .
|
||||
|
||||
# Commits the tracked changes and prepares them to be pushed to a remote repository.
|
||||
git commit -m "$release_note"
|
||||
|
||||
# Sets the new remote
|
||||
git_remote=$(git remote)
|
||||
if [ "$git_remote" = "" ]; then # git remote not defined
|
||||
|
||||
if [ "$GIT_TOKEN" = "" ]; then
|
||||
echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment."
|
||||
git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git
|
||||
else
|
||||
git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
git pull origin master
|
||||
|
||||
# Pushes (Forces) the changes in the local repository up to the remote repository
|
||||
echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git"
|
||||
git push origin master 2>&1 | grep -v 'To https'
|
18
cli/src/api/open-api/index.ts
generated
Normal file
18
cli/src/api/open-api/index.ts
generated
Normal file
|
@ -0,0 +1,18 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Immich
|
||||
* Immich API
|
||||
*
|
||||
* The version of the OpenAPI document: 1.89.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
export * from "./api";
|
||||
export * from "./configuration";
|
||||
|
37
cli/src/cli/base-command.ts
Normal file
37
cli/src/cli/base-command.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { ImmichApi } from '../api/client';
|
||||
import path from 'node:path';
|
||||
import { SessionService } from '../services/session.service';
|
||||
import { LoginError } from '../cores/errors/login-error';
|
||||
import { exit } from 'node:process';
|
||||
import os from 'os';
|
||||
import { ServerVersionResponseDto, UserResponseDto } from 'src/api/open-api';
|
||||
|
||||
export abstract class BaseCommand {
|
||||
protected sessionService!: SessionService;
|
||||
protected immichApi!: ImmichApi;
|
||||
protected user!: UserResponseDto;
|
||||
protected serverVersion!: ServerVersionResponseDto;
|
||||
|
||||
protected configDir;
|
||||
protected authPath;
|
||||
|
||||
constructor() {
|
||||
const userHomeDir = os.homedir();
|
||||
this.configDir = path.join(userHomeDir, '.config/immich/');
|
||||
this.sessionService = new SessionService(this.configDir);
|
||||
this.authPath = path.join(this.configDir, 'auth.yml');
|
||||
}
|
||||
|
||||
public async connect(): Promise<void> {
|
||||
try {
|
||||
this.immichApi = await this.sessionService.connect();
|
||||
} catch (error) {
|
||||
if (error instanceof LoginError) {
|
||||
console.log(error.message);
|
||||
exit(1);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
cli/src/commands/login/key.ts
Normal file
9
cli/src/commands/login/key.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { BaseCommand } from '../../cli/base-command';
|
||||
|
||||
export default class LoginKey extends BaseCommand {
|
||||
public async run(instanceUrl: string, apiKey: string): Promise<void> {
|
||||
console.log('Executing API key auth flow...');
|
||||
|
||||
await this.sessionService.keyLogin(instanceUrl, apiKey);
|
||||
}
|
||||
}
|
13
cli/src/commands/logout.ts
Normal file
13
cli/src/commands/logout.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { BaseCommand } from '../cli/base-command';
|
||||
|
||||
export default class Logout extends BaseCommand {
|
||||
public static readonly description = 'Logout and remove persisted credentials';
|
||||
|
||||
public async run(): Promise<void> {
|
||||
console.log('Executing logout flow...');
|
||||
|
||||
await this.sessionService.logout();
|
||||
|
||||
console.log('Successfully logged out');
|
||||
}
|
||||
}
|
19
cli/src/commands/server-info.ts
Normal file
19
cli/src/commands/server-info.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { BaseCommand } from '../cli/base-command';
|
||||
|
||||
export default class ServerInfo extends BaseCommand {
|
||||
public async run() {
|
||||
await this.connect();
|
||||
const { data: versionInfo } = await this.immichApi.serverInfoApi.getServerVersion();
|
||||
|
||||
console.log(`Server is running version ${versionInfo.major}.${versionInfo.minor}.${versionInfo.patch}`);
|
||||
|
||||
const { data: supportedmedia } = await this.immichApi.serverInfoApi.getSupportedMediaTypes();
|
||||
|
||||
console.log(`Supported image types: ${supportedmedia.image.map((extension) => extension.replace('.', ''))}`);
|
||||
|
||||
console.log(`Supported video types: ${supportedmedia.video.map((extension) => extension.replace('.', ''))}`);
|
||||
|
||||
const { data: statistics } = await this.immichApi.assetApi.getAssetStatistics();
|
||||
console.log(`Images: ${statistics.images}, Videos: ${statistics.videos}, Total: ${statistics.total}`);
|
||||
}
|
||||
}
|
160
cli/src/commands/upload.ts
Normal file
160
cli/src/commands/upload.ts
Normal file
|
@ -0,0 +1,160 @@
|
|||
import { Asset } from '../cores/models/asset';
|
||||
import { CrawlService } from '../services';
|
||||
import { UploadOptionsDto } from '../cores/dto/upload-options-dto';
|
||||
import { CrawlOptionsDto } from '../cores/dto/crawl-options-dto';
|
||||
|
||||
import cliProgress from 'cli-progress';
|
||||
import byteSize from 'byte-size';
|
||||
import { BaseCommand } from '../cli/base-command';
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
import FormData from 'form-data';
|
||||
|
||||
export default class Upload extends BaseCommand {
|
||||
uploadLength!: number;
|
||||
|
||||
public async run(paths: string[], options: UploadOptionsDto): Promise<void> {
|
||||
await this.connect();
|
||||
|
||||
const deviceId = 'CLI';
|
||||
|
||||
const formatResponse = await this.immichApi.serverInfoApi.getSupportedMediaTypes();
|
||||
const crawlService = new CrawlService(formatResponse.data.image, formatResponse.data.video);
|
||||
|
||||
const crawlOptions = new CrawlOptionsDto();
|
||||
crawlOptions.pathsToCrawl = paths;
|
||||
crawlOptions.recursive = options.recursive;
|
||||
crawlOptions.exclusionPatterns = options.exclusionPatterns;
|
||||
|
||||
const crawledFiles: string[] = await crawlService.crawl(crawlOptions);
|
||||
|
||||
if (crawledFiles.length === 0) {
|
||||
console.log('No assets found, exiting');
|
||||
return;
|
||||
}
|
||||
|
||||
const assetsToUpload = crawledFiles.map((path) => new Asset(path, deviceId));
|
||||
|
||||
const uploadProgress = new cliProgress.SingleBar(
|
||||
{
|
||||
format: '{bar} | {percentage}% | ETA: {eta_formatted} | {value_formatted}/{total_formatted}: {filename}',
|
||||
},
|
||||
cliProgress.Presets.shades_classic,
|
||||
);
|
||||
|
||||
let totalSize = 0;
|
||||
let sizeSoFar = 0;
|
||||
|
||||
let totalSizeUploaded = 0;
|
||||
let uploadCounter = 0;
|
||||
|
||||
for (const asset of assetsToUpload) {
|
||||
// Compute total size first
|
||||
await asset.process();
|
||||
totalSize += asset.fileSize;
|
||||
}
|
||||
|
||||
const existingAlbums = (await this.immichApi.albumApi.getAllAlbums()).data;
|
||||
|
||||
uploadProgress.start(totalSize, 0);
|
||||
uploadProgress.update({ value_formatted: 0, total_formatted: byteSize(totalSize) });
|
||||
|
||||
try {
|
||||
for (const asset of assetsToUpload) {
|
||||
uploadProgress.update({
|
||||
filename: asset.path,
|
||||
});
|
||||
|
||||
let skipUpload = false;
|
||||
if (!options.skipHash) {
|
||||
const assetBulkUploadCheckDto = { assets: [{ id: asset.path, checksum: await asset.hash() }] };
|
||||
|
||||
const checkResponse = await this.immichApi.assetApi.checkBulkUpload({
|
||||
assetBulkUploadCheckDto,
|
||||
});
|
||||
|
||||
skipUpload = checkResponse.data.results[0].action === 'reject';
|
||||
}
|
||||
|
||||
if (!skipUpload) {
|
||||
if (!options.dryRun) {
|
||||
const formData = asset.getUploadFormData();
|
||||
const res = await this.uploadAsset(formData);
|
||||
|
||||
if (options.album && asset.albumName) {
|
||||
let album = existingAlbums.find((album) => album.albumName === asset.albumName);
|
||||
if (!album) {
|
||||
const res = await this.immichApi.albumApi.createAlbum({
|
||||
createAlbumDto: { albumName: asset.albumName },
|
||||
});
|
||||
album = res.data;
|
||||
existingAlbums.push(album);
|
||||
}
|
||||
|
||||
await this.immichApi.albumApi.addAssetsToAlbum({ id: album.id, bulkIdsDto: { ids: [res.data.id] } });
|
||||
}
|
||||
}
|
||||
|
||||
totalSizeUploaded += asset.fileSize;
|
||||
uploadCounter++;
|
||||
}
|
||||
|
||||
sizeSoFar += asset.fileSize;
|
||||
|
||||
uploadProgress.update(sizeSoFar, { value_formatted: byteSize(sizeSoFar) });
|
||||
}
|
||||
} finally {
|
||||
uploadProgress.stop();
|
||||
}
|
||||
|
||||
let messageStart;
|
||||
if (options.dryRun) {
|
||||
messageStart = 'Would have';
|
||||
} else {
|
||||
messageStart = 'Successfully';
|
||||
}
|
||||
|
||||
if (uploadCounter === 0) {
|
||||
console.log('All assets were already uploaded, nothing to do.');
|
||||
} else {
|
||||
console.log(`${messageStart} uploaded ${uploadCounter} assets (${byteSize(totalSizeUploaded)})`);
|
||||
}
|
||||
if (options.delete) {
|
||||
if (options.dryRun) {
|
||||
console.log(`Would now have deleted assets, but skipped due to dry run`);
|
||||
} else {
|
||||
console.log('Deleting assets that have been uploaded...');
|
||||
const deletionProgress = new cliProgress.SingleBar(cliProgress.Presets.shades_classic);
|
||||
deletionProgress.start(crawledFiles.length, 0);
|
||||
|
||||
for (const asset of assetsToUpload) {
|
||||
if (!options.dryRun) {
|
||||
await asset.delete();
|
||||
}
|
||||
deletionProgress.increment();
|
||||
}
|
||||
deletionProgress.stop();
|
||||
console.log('Deletion complete');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async uploadAsset(data: FormData): Promise<axios.AxiosResponse> {
|
||||
const url = this.immichApi.apiConfiguration.instanceUrl + '/asset/upload';
|
||||
|
||||
const config: AxiosRequestConfig = {
|
||||
method: 'post',
|
||||
maxRedirects: 0,
|
||||
url,
|
||||
headers: {
|
||||
'x-api-key': this.immichApi.apiConfiguration.apiKey,
|
||||
...data.getHeaders(),
|
||||
},
|
||||
maxContentLength: Infinity,
|
||||
maxBodyLength: Infinity,
|
||||
data,
|
||||
};
|
||||
|
||||
const res = await axios(config);
|
||||
return res;
|
||||
}
|
||||
}
|
9
cli/src/cores/api-configuration.ts
Normal file
9
cli/src/cores/api-configuration.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
export class ApiConfiguration {
|
||||
public readonly instanceUrl!: string;
|
||||
public readonly apiKey!: string;
|
||||
|
||||
constructor(instanceUrl: string, apiKey: string) {
|
||||
this.instanceUrl = instanceUrl;
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
}
|
6
cli/src/cores/dto/crawl-options-dto.ts
Normal file
6
cli/src/cores/dto/crawl-options-dto.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export class CrawlOptionsDto {
|
||||
pathsToCrawl!: string[];
|
||||
recursive? = false;
|
||||
includeHidden? = false;
|
||||
exclusionPatterns?: string[];
|
||||
}
|
9
cli/src/cores/dto/upload-options-dto.ts
Normal file
9
cli/src/cores/dto/upload-options-dto.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
export class UploadOptionsDto {
|
||||
recursive = false;
|
||||
exclusionPatterns!: string[];
|
||||
dryRun = false;
|
||||
skipHash = false;
|
||||
delete = false;
|
||||
readOnly = true;
|
||||
album = false;
|
||||
}
|
11
cli/src/cores/errors/login-error.ts
Normal file
11
cli/src/cores/errors/login-error.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
export class LoginError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
|
||||
// assign the error class name in your custom error (as a shortcut)
|
||||
this.name = this.constructor.name;
|
||||
|
||||
// capturing the stack trace keeps the reference to your error class
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
}
|
1
cli/src/cores/index.ts
Normal file
1
cli/src/cores/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './models';
|
100
cli/src/cores/models/asset.ts
Normal file
100
cli/src/cores/models/asset.ts
Normal file
|
@ -0,0 +1,100 @@
|
|||
import * as fs from 'fs';
|
||||
import { basename } from 'node:path';
|
||||
import crypto from 'crypto';
|
||||
import Os from 'os';
|
||||
import FormData from 'form-data';
|
||||
|
||||
export class Asset {
|
||||
readonly path: string;
|
||||
readonly deviceId!: string;
|
||||
|
||||
assetData?: fs.ReadStream;
|
||||
deviceAssetId?: string;
|
||||
fileCreatedAt?: string;
|
||||
fileModifiedAt?: string;
|
||||
sidecarData?: fs.ReadStream;
|
||||
sidecarPath?: string;
|
||||
fileSize!: number;
|
||||
albumName?: string;
|
||||
|
||||
constructor(path: string, deviceId: string) {
|
||||
this.path = path;
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
async process() {
|
||||
const stats = await fs.promises.stat(this.path);
|
||||
this.deviceAssetId = `${basename(this.path)}-${stats.size}`.replace(/\s+/g, '');
|
||||
this.fileCreatedAt = stats.mtime.toISOString();
|
||||
this.fileModifiedAt = stats.mtime.toISOString();
|
||||
this.fileSize = stats.size;
|
||||
this.albumName = this.extractAlbumName();
|
||||
|
||||
this.assetData = this.getReadStream(this.path);
|
||||
|
||||
// TODO: doesn't xmp replace the file extension? Will need investigation
|
||||
const sideCarPath = `${this.path}.xmp`;
|
||||
try {
|
||||
fs.accessSync(sideCarPath, fs.constants.R_OK);
|
||||
this.sidecarData = this.getReadStream(sideCarPath);
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
getUploadFormData(): FormData {
|
||||
if (!this.assetData) throw new Error('Asset data not set');
|
||||
if (!this.deviceAssetId) throw new Error('Device asset id not set');
|
||||
if (!this.fileCreatedAt) throw new Error('File created at not set');
|
||||
if (!this.fileModifiedAt) throw new Error('File modified at not set');
|
||||
if (!this.deviceId) throw new Error('Device id not set');
|
||||
|
||||
const data: any = {
|
||||
assetData: this.assetData as any,
|
||||
deviceAssetId: this.deviceAssetId,
|
||||
deviceId: this.deviceId,
|
||||
fileCreatedAt: this.fileCreatedAt,
|
||||
fileModifiedAt: this.fileModifiedAt,
|
||||
isFavorite: String(false),
|
||||
};
|
||||
const formData = new FormData();
|
||||
|
||||
for (const prop in data) {
|
||||
formData.append(prop, data[prop]);
|
||||
}
|
||||
|
||||
if (this.sidecarData) {
|
||||
formData.append('sidecarData', this.sidecarData);
|
||||
}
|
||||
|
||||
return formData;
|
||||
}
|
||||
|
||||
private getReadStream(path: string): fs.ReadStream {
|
||||
return fs.createReadStream(path);
|
||||
}
|
||||
|
||||
async delete(): Promise<void> {
|
||||
return fs.promises.unlink(this.path);
|
||||
}
|
||||
|
||||
public async hash(): Promise<string> {
|
||||
const sha1 = (filePath: string) => {
|
||||
const hash = crypto.createHash('sha1');
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
const rs = fs.createReadStream(filePath);
|
||||
rs.on('error', reject);
|
||||
rs.on('data', (chunk) => hash.update(chunk));
|
||||
rs.on('end', () => resolve(hash.digest('hex')));
|
||||
});
|
||||
};
|
||||
|
||||
return await sha1(this.path);
|
||||
}
|
||||
|
||||
private extractAlbumName(): string {
|
||||
if (Os.platform() === 'win32') {
|
||||
return this.path.split('\\').slice(-2)[0];
|
||||
} else {
|
||||
return this.path.split('/').slice(-2)[0];
|
||||
}
|
||||
}
|
||||
}
|
1
cli/src/cores/models/index.ts
Normal file
1
cli/src/cores/models/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './asset';
|
59
cli/src/index.ts
Normal file
59
cli/src/index.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
#! /usr/bin/env node
|
||||
|
||||
import { program, Option } from 'commander';
|
||||
import Upload from './commands/upload';
|
||||
import ServerInfo from './commands/server-info';
|
||||
import LoginKey from './commands/login/key';
|
||||
import Logout from './commands/logout';
|
||||
import { version } from '../package.json';
|
||||
|
||||
program.name('immich').description('Immich command line interface').version(version);
|
||||
|
||||
program
|
||||
.command('upload')
|
||||
.description('Upload assets')
|
||||
.usage('[options] [paths...]')
|
||||
.addOption(new Option('-r, --recursive', 'Recursive').env('IMMICH_RECURSIVE').default(false))
|
||||
.addOption(new Option('-i, --ignore [paths...]', 'Paths to ignore').env('IMMICH_IGNORE_PATHS'))
|
||||
.addOption(new Option('-h, --skip-hash', "Don't hash files before upload").env('IMMICH_SKIP_HASH').default(false))
|
||||
.addOption(
|
||||
new Option('-a, --album', 'Automatically create albums based on folder name')
|
||||
.env('IMMICH_AUTO_CREATE_ALBUM')
|
||||
.default(false),
|
||||
)
|
||||
.addOption(
|
||||
new Option('-n, --dry-run', "Don't perform any actions, just show what will be done")
|
||||
.env('IMMICH_DRY_RUN')
|
||||
.default(false),
|
||||
)
|
||||
.addOption(new Option('--delete', 'Delete local assets after upload').env('IMMICH_DELETE_ASSETS'))
|
||||
.argument('[paths...]', 'One or more paths to assets to be uploaded')
|
||||
.action(async (paths, options) => {
|
||||
options.exclusionPatterns = options.ignore;
|
||||
await new Upload().run(paths, options);
|
||||
});
|
||||
|
||||
program
|
||||
.command('server-info')
|
||||
.description('Display server information')
|
||||
.action(async () => {
|
||||
await new ServerInfo().run();
|
||||
});
|
||||
|
||||
program
|
||||
.command('login-key')
|
||||
.description('Login using an API key')
|
||||
.argument('[instanceUrl]')
|
||||
.argument('[apiKey]')
|
||||
.action(async (paths, options) => {
|
||||
await new LoginKey().run(paths, options);
|
||||
});
|
||||
|
||||
program
|
||||
.command('logout')
|
||||
.description('Remove stored credentials')
|
||||
.action(async () => {
|
||||
await new Logout().run();
|
||||
});
|
||||
|
||||
program.parse(process.argv);
|
206
cli/src/services/crawl.service.spec.ts
Normal file
206
cli/src/services/crawl.service.spec.ts
Normal file
|
@ -0,0 +1,206 @@
|
|||
import mockfs from 'mock-fs';
|
||||
import { CrawlOptionsDto } from 'src/cores/dto/crawl-options-dto';
|
||||
import { CrawlService } from '.';
|
||||
|
||||
interface Test {
|
||||
test: string;
|
||||
options: CrawlOptionsDto;
|
||||
files: Record<string, boolean>;
|
||||
}
|
||||
|
||||
const cwd = process.cwd();
|
||||
|
||||
const tests: Test[] = [
|
||||
{
|
||||
test: 'should return empty when crawling an empty path list',
|
||||
options: {
|
||||
pathsToCrawl: [],
|
||||
},
|
||||
files: {},
|
||||
},
|
||||
{
|
||||
test: 'should crawl a single path',
|
||||
options: {
|
||||
pathsToCrawl: ['/photos/'],
|
||||
},
|
||||
files: {
|
||||
'/photos/image.jpg': true,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: 'should exclude by file extension',
|
||||
options: {
|
||||
pathsToCrawl: ['/photos/'],
|
||||
exclusionPatterns: ['**/*.tif'],
|
||||
},
|
||||
files: {
|
||||
'/photos/image.jpg': true,
|
||||
'/photos/image.tif': false,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: 'should exclude by file extension without case sensitivity',
|
||||
options: {
|
||||
pathsToCrawl: ['/photos/'],
|
||||
exclusionPatterns: ['**/*.TIF'],
|
||||
},
|
||||
files: {
|
||||
'/photos/image.jpg': true,
|
||||
'/photos/image.tif': false,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: 'should exclude by folder',
|
||||
options: {
|
||||
pathsToCrawl: ['/photos/'],
|
||||
exclusionPatterns: ['**/raw/**'],
|
||||
},
|
||||
files: {
|
||||
'/photos/image.jpg': true,
|
||||
'/photos/raw/image.jpg': false,
|
||||
'/photos/raw2/image.jpg': true,
|
||||
'/photos/folder/raw/image.jpg': false,
|
||||
'/photos/crawl/image.jpg': true,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: 'should crawl multiple paths',
|
||||
options: {
|
||||
pathsToCrawl: ['/photos/', '/images/', '/albums/'],
|
||||
},
|
||||
files: {
|
||||
'/photos/image1.jpg': true,
|
||||
'/images/image2.jpg': true,
|
||||
'/albums/image3.jpg': true,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: 'should support globbing paths',
|
||||
options: {
|
||||
pathsToCrawl: ['/photos*'],
|
||||
},
|
||||
files: {
|
||||
'/photos1/image1.jpg': true,
|
||||
'/photos2/image2.jpg': true,
|
||||
'/images/image3.jpg': false,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: 'should crawl a single path without trailing slash',
|
||||
options: {
|
||||
pathsToCrawl: ['/photos'],
|
||||
},
|
||||
files: {
|
||||
'/photos/image.jpg': true,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: 'should crawl a single path',
|
||||
options: {
|
||||
pathsToCrawl: ['/photos/'],
|
||||
},
|
||||
files: {
|
||||
'/photos/image.jpg': true,
|
||||
'/photos/subfolder/image1.jpg': true,
|
||||
'/photos/subfolder/image2.jpg': true,
|
||||
'/image1.jpg': false,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: 'should filter file extensions',
|
||||
options: {
|
||||
pathsToCrawl: ['/photos/'],
|
||||
},
|
||||
files: {
|
||||
'/photos/image.jpg': true,
|
||||
'/photos/image.txt': false,
|
||||
'/photos/1': false,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: 'should include photo and video extensions',
|
||||
options: {
|
||||
pathsToCrawl: ['/photos/', '/videos/'],
|
||||
},
|
||||
files: {
|
||||
'/photos/image.jpg': true,
|
||||
'/photos/image.jpeg': true,
|
||||
'/photos/image.heic': true,
|
||||
'/photos/image.heif': true,
|
||||
'/photos/image.png': true,
|
||||
'/photos/image.gif': true,
|
||||
'/photos/image.tif': true,
|
||||
'/photos/image.tiff': true,
|
||||
'/photos/image.webp': true,
|
||||
'/photos/image.dng': true,
|
||||
'/photos/image.nef': true,
|
||||
'/videos/video.mp4': true,
|
||||
'/videos/video.mov': true,
|
||||
'/videos/video.webm': true,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: 'should check file extensions without case sensitivity',
|
||||
options: {
|
||||
pathsToCrawl: ['/photos/'],
|
||||
},
|
||||
files: {
|
||||
'/photos/image.jpg': true,
|
||||
'/photos/image.Jpg': true,
|
||||
'/photos/image.jpG': true,
|
||||
'/photos/image.JPG': true,
|
||||
'/photos/image.jpEg': true,
|
||||
'/photos/image.TIFF': true,
|
||||
'/photos/image.tif': true,
|
||||
'/photos/image.dng': true,
|
||||
'/photos/image.NEF': true,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: 'should normalize the path',
|
||||
options: {
|
||||
pathsToCrawl: ['/photos/1/../2'],
|
||||
},
|
||||
files: {
|
||||
'/photos/1/image.jpg': false,
|
||||
'/photos/2/image.jpg': true,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: 'should return absolute paths',
|
||||
options: {
|
||||
pathsToCrawl: ['photos'],
|
||||
},
|
||||
files: {
|
||||
[`${cwd}/photos/1.jpg`]: true,
|
||||
[`${cwd}/photos/2.jpg`]: true,
|
||||
[`/photos/3.jpg`]: false,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
describe(CrawlService.name, () => {
|
||||
const sut = new CrawlService(
|
||||
['.jpg', '.jpeg', '.png', '.heif', '.heic', '.tif', '.nef', '.webp', '.tiff', '.dng', '.gif'],
|
||||
['.mov', '.mp4', '.webm'],
|
||||
);
|
||||
|
||||
afterEach(() => {
|
||||
mockfs.restore();
|
||||
});
|
||||
|
||||
describe('crawl', () => {
|
||||
for (const { test, options, files } of tests) {
|
||||
it(test, async () => {
|
||||
mockfs(Object.fromEntries(Object.keys(files).map((file) => [file, ''])));
|
||||
|
||||
const actual = await sut.crawl(options);
|
||||
const expected = Object.entries(files)
|
||||
.filter((entry) => entry[1])
|
||||
.map(([file]) => file);
|
||||
|
||||
expect(actual.sort()).toEqual(expected.sort());
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
28
cli/src/services/crawl.service.ts
Normal file
28
cli/src/services/crawl.service.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { CrawlOptionsDto } from 'src/cores/dto/crawl-options-dto';
|
||||
import { glob } from 'glob';
|
||||
|
||||
export class CrawlService {
|
||||
private readonly extensions!: string[];
|
||||
|
||||
constructor(image: string[], video: string[]) {
|
||||
this.extensions = image.concat(video).map((extension) => extension.replace('.', ''));
|
||||
}
|
||||
|
||||
crawl(crawlOptions: CrawlOptionsDto): Promise<string[]> {
|
||||
const { pathsToCrawl, exclusionPatterns, includeHidden } = crawlOptions;
|
||||
if (!pathsToCrawl) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
const base = pathsToCrawl.length === 1 ? pathsToCrawl[0] : `{${pathsToCrawl.join(',')}}`;
|
||||
const extensions = `*{${this.extensions}}`;
|
||||
|
||||
return glob(`${base}/**/${extensions}`, {
|
||||
absolute: true,
|
||||
nocase: true,
|
||||
nodir: true,
|
||||
dot: includeHidden,
|
||||
ignore: exclusionPatterns,
|
||||
});
|
||||
}
|
||||
}
|
1
cli/src/services/index.ts
Normal file
1
cli/src/services/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './crawl.service';
|
95
cli/src/services/session.service.spec.ts
Normal file
95
cli/src/services/session.service.spec.ts
Normal file
|
@ -0,0 +1,95 @@
|
|||
import { SessionService } from './session.service';
|
||||
import mockfs from 'mock-fs';
|
||||
import fs from 'node:fs';
|
||||
import yaml from 'yaml';
|
||||
import { LoginError } from '../cores/errors/login-error';
|
||||
|
||||
const mockPingServer = jest.fn(() => Promise.resolve({ data: { res: 'pong' } }));
|
||||
const mockUserInfo = jest.fn(() => Promise.resolve({ data: { email: 'admin@example.com' } }));
|
||||
|
||||
jest.mock('../api/open-api', () => {
|
||||
return {
|
||||
__esModule: true,
|
||||
...jest.requireActual('../api/open-api'),
|
||||
UserApi: jest.fn().mockImplementation(() => {
|
||||
return { getMyUserInfo: mockUserInfo };
|
||||
}),
|
||||
ServerInfoApi: jest.fn().mockImplementation(() => {
|
||||
return { pingServer: mockPingServer };
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('SessionService', () => {
|
||||
let sessionService: SessionService;
|
||||
beforeAll(() => {
|
||||
// Write a dummy output before mock-fs to prevent some annoying errors
|
||||
console.log();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
const configDir = '/config';
|
||||
sessionService = new SessionService(configDir);
|
||||
});
|
||||
|
||||
it('should connect to immich', async () => {
|
||||
mockfs({
|
||||
'/config/auth.yml': 'apiKey: pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg\ninstanceUrl: https://test/api',
|
||||
});
|
||||
await sessionService.connect();
|
||||
expect(mockPingServer).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should error if no auth file exists', async () => {
|
||||
mockfs();
|
||||
await sessionService.connect().catch((error) => {
|
||||
expect(error.message).toEqual('No auth file exist. Please login first');
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if auth file is missing instance URl', async () => {
|
||||
mockfs({
|
||||
'/config/auth.yml': 'foo: pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg\napiKey: https://test/api',
|
||||
});
|
||||
await sessionService.connect().catch((error) => {
|
||||
expect(error).toBeInstanceOf(LoginError);
|
||||
expect(error.message).toEqual('Instance URL missing in auth config file /config/auth.yml');
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if auth file is missing api key', async () => {
|
||||
mockfs({
|
||||
'/config/auth.yml': 'instanceUrl: pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg\nbar: https://test/api',
|
||||
});
|
||||
await sessionService.connect().catch((error) => {
|
||||
expect(error).toBeInstanceOf(LoginError);
|
||||
expect(error.message).toEqual('API key missing in auth config file /config/auth.yml');
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should create auth file when logged in', async () => {
|
||||
mockfs();
|
||||
|
||||
await sessionService.keyLogin('https://test/api', 'pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg');
|
||||
|
||||
const data: string = await fs.promises.readFile('/config/auth.yml', 'utf8');
|
||||
const authConfig = yaml.parse(data);
|
||||
expect(authConfig.instanceUrl).toBe('https://test/api');
|
||||
expect(authConfig.apiKey).toBe('pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg');
|
||||
});
|
||||
|
||||
it('should delete auth file when logging out', async () => {
|
||||
mockfs({
|
||||
'/config/auth.yml': 'apiKey: pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg\ninstanceUrl: https://test/api',
|
||||
});
|
||||
await sessionService.logout();
|
||||
|
||||
await fs.promises.access('/auth.yml', fs.constants.F_OK).catch((error) => {
|
||||
expect(error.message).toContain('ENOENT');
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockfs.restore();
|
||||
});
|
||||
});
|
88
cli/src/services/session.service.ts
Normal file
88
cli/src/services/session.service.ts
Normal file
|
@ -0,0 +1,88 @@
|
|||
import fs from 'node:fs';
|
||||
import yaml from 'yaml';
|
||||
import path from 'node:path';
|
||||
import { ImmichApi } from '../api/client';
|
||||
import { LoginError } from '../cores/errors/login-error';
|
||||
|
||||
export class SessionService {
|
||||
readonly configDir: string;
|
||||
readonly authPath!: string;
|
||||
private api!: ImmichApi;
|
||||
|
||||
constructor(configDir: string) {
|
||||
this.configDir = configDir;
|
||||
this.authPath = path.join(this.configDir, 'auth.yml');
|
||||
}
|
||||
|
||||
public async connect(): Promise<ImmichApi> {
|
||||
await fs.promises.access(this.authPath, fs.constants.F_OK).catch((error) => {
|
||||
if (error.code === 'ENOENT') {
|
||||
throw new LoginError('No auth file exist. Please login first');
|
||||
}
|
||||
});
|
||||
|
||||
const data: string = await fs.promises.readFile(this.authPath, 'utf8');
|
||||
const parsedConfig = yaml.parse(data);
|
||||
const instanceUrl: string = parsedConfig.instanceUrl;
|
||||
const apiKey: string = parsedConfig.apiKey;
|
||||
|
||||
if (!instanceUrl) {
|
||||
throw new LoginError('Instance URL missing in auth config file ' + this.authPath);
|
||||
}
|
||||
|
||||
if (!apiKey) {
|
||||
throw new LoginError('API key missing in auth config file ' + this.authPath);
|
||||
}
|
||||
|
||||
this.api = new ImmichApi(instanceUrl, apiKey);
|
||||
|
||||
await this.ping();
|
||||
|
||||
return this.api;
|
||||
}
|
||||
|
||||
public async keyLogin(instanceUrl: string, apiKey: string): Promise<ImmichApi> {
|
||||
this.api = new ImmichApi(instanceUrl, apiKey);
|
||||
|
||||
// Check if server and api key are valid
|
||||
const { data: userInfo } = await this.api.userApi.getMyUserInfo().catch((error) => {
|
||||
throw new LoginError(`Failed to connect to server ${instanceUrl}: ${error.message}`);
|
||||
});
|
||||
|
||||
console.log(`Logged in as ${userInfo.email}`);
|
||||
|
||||
if (!fs.existsSync(this.configDir)) {
|
||||
// Create config folder if it doesn't exist
|
||||
const created = await fs.promises.mkdir(this.configDir, { recursive: true });
|
||||
if (!created) {
|
||||
throw new Error(`Failed to create config folder ${this.configDir}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!fs.existsSync(this.configDir)) {
|
||||
console.error('waah');
|
||||
}
|
||||
|
||||
fs.writeFileSync(this.authPath, yaml.stringify({ instanceUrl, apiKey }));
|
||||
|
||||
console.log('Wrote auth info to ' + this.authPath);
|
||||
return this.api;
|
||||
}
|
||||
|
||||
public async logout(): Promise<void> {
|
||||
if (fs.existsSync(this.authPath)) {
|
||||
fs.unlinkSync(this.authPath);
|
||||
console.log('Removed auth file ' + this.authPath);
|
||||
}
|
||||
}
|
||||
|
||||
private async ping(): Promise<void> {
|
||||
const { data: pingResponse } = await this.api.serverInfoApi.pingServer().catch((error) => {
|
||||
throw new Error(`Failed to connect to server ${this.api.apiConfiguration.instanceUrl}: ${error.message}`);
|
||||
});
|
||||
|
||||
if (pingResponse.res !== 'pong') {
|
||||
throw new Error('Unexpected ping reply');
|
||||
}
|
||||
}
|
||||
}
|
3
cli/testSetup.js
Normal file
3
cli/testSetup.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
// add all jest-extended matchers
|
||||
import * as matchers from 'jest-extended';
|
||||
expect.extend(matchers);
|
4
cli/tsconfig.build.json
Normal file
4
cli/tsconfig.build.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["dist", "node_modules", "upload", "test", "**/*spec.ts"]
|
||||
}
|
25
cli/tsconfig.json
Normal file
25
cli/tsconfig.json
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "Node16",
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true,
|
||||
"target": "es2022",
|
||||
"moduleResolution": "node16",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"incremental": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@test": ["test"],
|
||||
"@test/*": ["test/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["dist", "node_modules", "upload"]
|
||||
}
|
BIN
design/immich-screenshots.png
Normal file
BIN
design/immich-screenshots.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 MiB |
32
dev-setup.md
32
dev-setup.md
|
@ -1,32 +0,0 @@
|
|||
# Development Setup
|
||||
|
||||
## Lint / format extensions
|
||||
|
||||
Setting these in the IDE give a better developer experience auto-formatting code on save and providing instant feedback on lint issues.
|
||||
|
||||
### VSCode
|
||||
Install Prettier, ESLint and Svelte extensions.
|
||||
|
||||
in User `settings.json` (`cmd + shift + p` and search for Open User Settings JSON) add the following:
|
||||
|
||||
```json
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"[javascript][typescript][css]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.tabSize": 2,
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[svelte]": {
|
||||
"editor.defaultFormatter": "svelte.svelte-vscode",
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
"svelte.enable-ts-plugin": true,
|
||||
"eslint.validate": ["javascript", "svelte"]
|
||||
}
|
||||
```
|
||||
|
||||
## Running tests / checks
|
||||
|
||||
In both server and web:
|
||||
`npm run check:all`
|
|
@ -1,79 +0,0 @@
|
|||
###################################################################################
|
||||
# Database
|
||||
###################################################################################
|
||||
|
||||
DB_HOSTNAME=immich_postgres
|
||||
DB_USERNAME=postgres
|
||||
DB_PASSWORD=postgres
|
||||
DB_DATABASE_NAME=immich
|
||||
|
||||
# Optional Database settings:
|
||||
# DB_PORT=5432
|
||||
|
||||
|
||||
|
||||
|
||||
###################################################################################
|
||||
# Redis
|
||||
###################################################################################
|
||||
|
||||
REDIS_HOSTNAME=immich_redis
|
||||
|
||||
# Optional Redis settings:
|
||||
# REDIS_PORT=6379
|
||||
# REDIS_DBINDEX=0
|
||||
# REDIS_PASSWORD=
|
||||
# REDIS_SOCKET=
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
###################################################################################
|
||||
# Upload File Config
|
||||
###################################################################################
|
||||
|
||||
UPLOAD_LOCATION=absolute_location_on_your_machine_where_you_want_to_store_the_backup
|
||||
|
||||
|
||||
###################################################################################
|
||||
# Log message level - [simple|verbose]
|
||||
###################################################################################
|
||||
|
||||
LOG_LEVEL=simple
|
||||
|
||||
|
||||
###################################################################################
|
||||
# JWT SECRET
|
||||
###################################################################################
|
||||
|
||||
JWT_SECRET=randomstringthatissolongandpowerfulthatnoonecanguess
|
||||
|
||||
|
||||
|
||||
|
||||
###################################################################################
|
||||
# MAPBOX
|
||||
####################################################################################
|
||||
|
||||
# ENABLE_MAPBOX is either true of false -> if true, you have to provide MAPBOX_KEY
|
||||
ENABLE_MAPBOX=false
|
||||
MAPBOX_KEY=
|
||||
|
||||
|
||||
####################################################################################
|
||||
# WEB - Optional
|
||||
####################################################################################
|
||||
|
||||
# Custom message on the login page, should be written in HTML form.
|
||||
# For example PUBLIC_LOGIN_PAGE_MESSAGE="This is a demo instance of Immich.<br><br>Email: <i>demo@demo.de</i><br>Password: <i>demo</i>"
|
||||
|
||||
PUBLIC_LOGIN_PAGE_MESSAGE=
|
||||
|
||||
# For correctly display your local time zone on the web, you can set the time zone here.
|
||||
# Should work fine by default value, however, in case of incorrect timezone in EXIF, this value
|
||||
# should be set to the correct timezone.
|
||||
# Command to get timezone:
|
||||
# - Linux: curl -s http://ip-api.com/json/ | grep -oP '(?<=timezone":")(.*?)(?=")'
|
||||
|
||||
# TZ=Etc/UTC
|
|
@ -1,22 +0,0 @@
|
|||
# Database
|
||||
DB_HOSTNAME=immich-database-test
|
||||
DB_USERNAME=postgres
|
||||
DB_PASSWORD=postgres
|
||||
DB_DATABASE_NAME=e2e_test
|
||||
|
||||
# Redis
|
||||
REDIS_HOSTNAME=immich_redis_test
|
||||
|
||||
# Upload File Config
|
||||
UPLOAD_LOCATION=./upload
|
||||
|
||||
# JWT SECRET
|
||||
JWT_SECRET=randomstringthatissolongandpowerfulthatnoonecanguess
|
||||
|
||||
# MAPBOX
|
||||
## ENABLE_MAPBOX is either true of false -> if true, you have to provide MAPBOX_KEY
|
||||
ENABLE_MAPBOX=false
|
||||
|
||||
# WEB
|
||||
MAPBOX_KEY=
|
||||
VITE_SERVER_ENDPOINT=http://localhost:2283/api
|
5
docker/README.md
Normal file
5
docker/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
> [!CAUTION]
|
||||
> Make sure to use the docker-compose.yml of the current release:
|
||||
> https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||
>
|
||||
> The compose file on main may not be compatible with the latest release.
|
|
@ -1,113 +1,128 @@
|
|||
# See:
|
||||
# - https://immich.app/docs/developer/setup
|
||||
# - https://immich.app/docs/developer/troubleshooting
|
||||
|
||||
version: "3.8"
|
||||
|
||||
name: immich-dev
|
||||
|
||||
x-server-build: &server-common
|
||||
image: immich-server-dev:latest
|
||||
build:
|
||||
context: ../
|
||||
dockerfile: server/Dockerfile
|
||||
target: dev
|
||||
volumes:
|
||||
- ../server:/usr/src/app
|
||||
- ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
|
||||
- /usr/src/app/node_modules
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 1048576
|
||||
hard: 1048576
|
||||
|
||||
services:
|
||||
immich-server:
|
||||
image: immich-server-dev:latest
|
||||
build:
|
||||
context: ../server
|
||||
dockerfile: Dockerfile
|
||||
target: builder
|
||||
command: npm run start:dev immich
|
||||
volumes:
|
||||
- ../server:/usr/src/app
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
- /usr/src/app/node_modules
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
container_name: immich_server
|
||||
command: npm run start:debug immich
|
||||
<<: *server-common
|
||||
ports:
|
||||
- 3001:3001
|
||||
- 9230:9230
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
|
||||
immich-machine-learning:
|
||||
image: immich-machine-learning-dev:latest
|
||||
build:
|
||||
context: ../machine-learning
|
||||
dockerfile: Dockerfile
|
||||
target: builder
|
||||
command: npm run start:dev
|
||||
volumes:
|
||||
- ../machine-learning:/usr/src/app
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
- /usr/src/app/node_modules
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
depends_on:
|
||||
- database
|
||||
- typesense
|
||||
|
||||
immich-microservices:
|
||||
image: immich-microservices:latest
|
||||
build:
|
||||
context: ../server
|
||||
dockerfile: Dockerfile
|
||||
target: builder
|
||||
command: npm run start:dev microservices
|
||||
volumes:
|
||||
- ../server:/usr/src/app
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
- /usr/src/app/node_modules
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
container_name: immich_microservices
|
||||
command: npm run start:debug microservices
|
||||
<<: *server-common
|
||||
# extends:
|
||||
# file: hwaccel.yml
|
||||
# service: hwaccel
|
||||
ports:
|
||||
- 9231:9230
|
||||
depends_on:
|
||||
- database
|
||||
- immich-server
|
||||
- typesense
|
||||
|
||||
immich-web:
|
||||
container_name: immich_web
|
||||
image: immich-web-dev:1.9.0
|
||||
build:
|
||||
context: ../web
|
||||
dockerfile: Dockerfile
|
||||
target: dev
|
||||
command: npm run dev --host
|
||||
command: "node ./node_modules/.bin/vite dev --host 0.0.0.0 --port 3000"
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- 3000:3000
|
||||
- 2283:3000
|
||||
- 24678:24678
|
||||
volumes:
|
||||
- ../web:/usr/src/app
|
||||
- /usr/src/app/node_modules
|
||||
restart: always
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 1048576
|
||||
hard: 1048576
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- immich-server
|
||||
|
||||
immich-machine-learning:
|
||||
container_name: immich_machine_learning
|
||||
image: immich-machine-learning-dev:latest
|
||||
build:
|
||||
context: ../machine-learning
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- 3003:3003
|
||||
volumes:
|
||||
- ../machine-learning:/usr/src/app
|
||||
- model-cache:/cache
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
depends_on:
|
||||
- database
|
||||
restart: unless-stopped
|
||||
|
||||
typesense:
|
||||
container_name: immich_typesense
|
||||
image: typesense/typesense:0.24.1@sha256:9bcff2b829f12074426ca044b56160ca9d777a0c488303469143dd9f8259d4dd
|
||||
environment:
|
||||
- TYPESENSE_API_KEY=${TYPESENSE_API_KEY}
|
||||
- TYPESENSE_DATA_DIR=/data
|
||||
# remove this to get debug messages
|
||||
- GLOG_minloglevel=1
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}/typesense:/data
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: redis:6.2
|
||||
image: redis:6.2-alpine@sha256:60e49e22fa5706cd8df7d5e0bc50ee9bab7c608039fa653c4d961014237cca46
|
||||
|
||||
database:
|
||||
container_name: immich_postgres
|
||||
image: postgres:14
|
||||
image: postgres:14-alpine@sha256:6a0e35296341e676fe6bd8d236c72afffe2dfe3d7eb9c2405c0f3fc04500cd07
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
POSTGRES_USER: ${DB_USERNAME}
|
||||
POSTGRES_DB: ${DB_DATABASE_NAME}
|
||||
PG_DATA: /var/lib/postgresql/data
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
immich-proxy:
|
||||
container_name: immich_proxy
|
||||
image: immich-proxy-dev:latest
|
||||
build:
|
||||
context: ../nginx
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- 2283:8080
|
||||
logging:
|
||||
driver: none
|
||||
depends_on:
|
||||
- immich-server
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
model-cache:
|
||||
|
|
85
docker/docker-compose.prod.yml
Normal file
85
docker/docker-compose.prod.yml
Normal file
|
@ -0,0 +1,85 @@
|
|||
version: "3.8"
|
||||
|
||||
name: immich-prod
|
||||
|
||||
x-server-build: &server-common
|
||||
image: immich-server:latest
|
||||
build:
|
||||
context: ../
|
||||
dockerfile: server/Dockerfile
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
|
||||
services:
|
||||
immich-server:
|
||||
container_name: immich_server
|
||||
command: [ "./start-server.sh" ]
|
||||
<<: *server-common
|
||||
ports:
|
||||
- 2283:3001
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
- typesense
|
||||
|
||||
immich-microservices:
|
||||
container_name: immich_microservices
|
||||
command: [ "./start-microservices.sh" ]
|
||||
<<: *server-common
|
||||
# extends:
|
||||
# file: hwaccel.yml
|
||||
# service: hwaccel
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
- typesense
|
||||
- immich-server
|
||||
|
||||
immich-machine-learning:
|
||||
container_name: immich_machine_learning
|
||||
image: immich-machine-learning:latest
|
||||
build:
|
||||
context: ../machine-learning
|
||||
dockerfile: Dockerfile
|
||||
volumes:
|
||||
- model-cache:/cache
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
|
||||
typesense:
|
||||
container_name: immich_typesense
|
||||
image: typesense/typesense:0.24.1@sha256:9bcff2b829f12074426ca044b56160ca9d777a0c488303469143dd9f8259d4dd
|
||||
environment:
|
||||
- TYPESENSE_API_KEY=${TYPESENSE_API_KEY}
|
||||
- TYPESENSE_DATA_DIR=/data
|
||||
# remove this to get debug messages
|
||||
- GLOG_minloglevel=1
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}/typesense:/data
|
||||
restart: always
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: redis:6.2-alpine@sha256:60e49e22fa5706cd8df7d5e0bc50ee9bab7c608039fa653c4d961014237cca46
|
||||
restart: always
|
||||
|
||||
database:
|
||||
container_name: immich_postgres
|
||||
image: postgres:14-alpine@sha256:6a0e35296341e676fe6bd8d236c72afffe2dfe3d7eb9c2405c0f3fc04500cd07
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
POSTGRES_USER: ${DB_USERNAME}
|
||||
POSTGRES_DB: ${DB_DATABASE_NAME}
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
model-cache:
|
|
@ -1,83 +0,0 @@
|
|||
version: "3.8"
|
||||
|
||||
services:
|
||||
immich-server:
|
||||
image: altran1502/immich-server:staging
|
||||
entrypoint: ["/bin/sh", "./start-server.sh"]
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
restart: always
|
||||
|
||||
immich-microservices:
|
||||
image: altran1502/immich-server:staging
|
||||
entrypoint: ["/bin/sh", "./start-microservices.sh"]
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
restart: always
|
||||
|
||||
immich-machine-learning:
|
||||
image: altran1502/immich-machine-learning:staging
|
||||
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
depends_on:
|
||||
- database
|
||||
restart: always
|
||||
|
||||
immich-web:
|
||||
image: altran1502/immich-web:staging
|
||||
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: redis:6.2
|
||||
restart: always
|
||||
|
||||
database:
|
||||
container_name: immich_postgres
|
||||
image: postgres:14
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
POSTGRES_USER: ${DB_USERNAME}
|
||||
POSTGRES_DB: ${DB_DATABASE_NAME}
|
||||
PG_DATA: /var/lib/postgresql/data
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
restart: always
|
||||
|
||||
immich-proxy:
|
||||
container_name: immich_proxy
|
||||
image: altran1502/immich-proxy:staging
|
||||
ports:
|
||||
- 2283:8080
|
||||
logging:
|
||||
driver: none
|
||||
depends_on:
|
||||
- immich-server
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
pgdata:
|
|
@ -1,46 +1,33 @@
|
|||
version: "3.8"
|
||||
|
||||
name: "immich-test-e2e"
|
||||
|
||||
services:
|
||||
immich-server-test:
|
||||
image: immich-server-test
|
||||
immich-server:
|
||||
image: immich-server-dev:latest
|
||||
build:
|
||||
context: ../server
|
||||
dockerfile: Dockerfile
|
||||
target: builder
|
||||
context: ../
|
||||
dockerfile: server/Dockerfile
|
||||
target: dev
|
||||
command: npm run test:e2e
|
||||
expose:
|
||||
- "3000"
|
||||
volumes:
|
||||
- ../server:/usr/src/app
|
||||
- /usr/src/app/node_modules
|
||||
env_file:
|
||||
- .env.test
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- DB_HOSTNAME=database
|
||||
- DB_USERNAME=postgres
|
||||
- DB_PASSWORD=postgres
|
||||
- DB_DATABASE_NAME=e2e_test
|
||||
- IMMICH_RUN_ALL_TESTS=true
|
||||
depends_on:
|
||||
- immich-redis-test
|
||||
- immich-database-test
|
||||
networks:
|
||||
- immich-test-network
|
||||
immich-redis-test:
|
||||
container_name: immich-redis-test
|
||||
image: redis:6.2
|
||||
networks:
|
||||
- immich-test-network
|
||||
immich-database-test:
|
||||
container_name: immich-database-test
|
||||
image: postgres:14
|
||||
env_file:
|
||||
- .env.test
|
||||
environment:
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
POSTGRES_USER: ${DB_USERNAME}
|
||||
POSTGRES_DB: ${DB_DATABASE_NAME}
|
||||
PG_DATA: /var/lib/postgresql/data
|
||||
volumes:
|
||||
- /var/lib/postgresql/data
|
||||
networks:
|
||||
- immich-test-network
|
||||
- database
|
||||
|
||||
networks:
|
||||
immich-test-network:
|
||||
database:
|
||||
image: postgres:14-alpine@sha256:6a0e35296341e676fe6bd8d236c72afffe2dfe3d7eb9c2405c0f3fc04500cd07
|
||||
command: -c fsync=off
|
||||
environment:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: e2e_test
|
||||
logging:
|
||||
driver: none
|
||||
|
|
|
@ -1,85 +1,91 @@
|
|||
version: "3.8"
|
||||
|
||||
#
|
||||
# WARNING: Make sure to use the docker-compose.yml of the current release:
|
||||
#
|
||||
# https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||
#
|
||||
# The compose file on main may not be compatible with the latest release.
|
||||
#
|
||||
|
||||
name: immich
|
||||
|
||||
services:
|
||||
immich-server:
|
||||
image: altran1502/immich-server:release
|
||||
entrypoint: ["/bin/sh", "./start-server.sh"]
|
||||
container_name: immich_server
|
||||
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
|
||||
command: [ "start.sh", "immich" ]
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
ports:
|
||||
- 2283:3001
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
- typesense
|
||||
restart: always
|
||||
|
||||
immich-microservices:
|
||||
image: altran1502/immich-server:release
|
||||
entrypoint: ["/bin/sh", "./start-microservices.sh"]
|
||||
container_name: immich_microservices
|
||||
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
|
||||
# extends:
|
||||
# file: hwaccel.yml
|
||||
# service: hwaccel
|
||||
command: [ "start.sh", "microservices" ]
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
- typesense
|
||||
restart: always
|
||||
|
||||
immich-machine-learning:
|
||||
image: altran1502/immich-machine-learning:release
|
||||
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
||||
container_name: immich_machine_learning
|
||||
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
- model-cache:/cache
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
depends_on:
|
||||
- database
|
||||
restart: always
|
||||
|
||||
immich-web:
|
||||
image: altran1502/immich-web:release
|
||||
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
||||
env_file:
|
||||
- .env
|
||||
typesense:
|
||||
container_name: immich_typesense
|
||||
image: typesense/typesense:0.24.1@sha256:9bcff2b829f12074426ca044b56160ca9d777a0c488303469143dd9f8259d4dd
|
||||
environment:
|
||||
- PUBLIC_TZ=${TZ}
|
||||
- TYPESENSE_API_KEY=${TYPESENSE_API_KEY}
|
||||
- TYPESENSE_DATA_DIR=/data
|
||||
# remove this to get debug messages
|
||||
- GLOG_minloglevel=1
|
||||
volumes:
|
||||
- tsdata:/data
|
||||
restart: always
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: redis:6.2
|
||||
image: redis:6.2-alpine@sha256:60e49e22fa5706cd8df7d5e0bc50ee9bab7c608039fa653c4d961014237cca46
|
||||
restart: always
|
||||
|
||||
database:
|
||||
container_name: immich_postgres
|
||||
image: postgres:14
|
||||
image: postgres:14-alpine@sha256:6a0e35296341e676fe6bd8d236c72afffe2dfe3d7eb9c2405c0f3fc04500cd07
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
POSTGRES_USER: ${DB_USERNAME}
|
||||
POSTGRES_DB: ${DB_DATABASE_NAME}
|
||||
PG_DATA: /var/lib/postgresql/data
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
restart: always
|
||||
|
||||
immich-proxy:
|
||||
container_name: immich_proxy
|
||||
image: altran1502/immich-proxy:release
|
||||
ports:
|
||||
- 2283:8080
|
||||
logging:
|
||||
driver: none
|
||||
depends_on:
|
||||
- immich-server
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
model-cache:
|
||||
tsdata:
|
||||
|
|
19
docker/example.env
Normal file
19
docker/example.env
Normal file
|
@ -0,0 +1,19 @@
|
|||
# You can find documentation for all the supported env variables at https://immich.app/docs/install/environment-variables
|
||||
|
||||
# The location where your uploaded files are stored
|
||||
UPLOAD_LOCATION=./library
|
||||
|
||||
# The Immich version to use. You can pin this to a specific version like "v1.71.0"
|
||||
IMMICH_VERSION=release
|
||||
|
||||
# Connection secrets for postgres and typesense. You should change these to random passwords
|
||||
TYPESENSE_API_KEY=some-random-text
|
||||
DB_PASSWORD=postgres
|
||||
|
||||
# The values below this line do not need to be changed
|
||||
###################################################################################
|
||||
DB_HOSTNAME=immich_postgres
|
||||
DB_USERNAME=postgres
|
||||
DB_DATABASE_NAME=immich
|
||||
|
||||
REDIS_HOSTNAME=immich_redis
|
24
docker/hwaccel-rkmpp.yml
Normal file
24
docker/hwaccel-rkmpp.yml
Normal file
|
@ -0,0 +1,24 @@
|
|||
version: "3.8"
|
||||
|
||||
# Hardware acceleration for transcoding using RKMPP for Rockchip SOCs
|
||||
# This is only needed if you want to use hardware acceleration for transcoding.
|
||||
# Supported host OS is Ubuntu Jammy 22.04 with custom ffmpeg from ppa:liujianfeng1994/rockchip-multimedia
|
||||
|
||||
services:
|
||||
hwaccel:
|
||||
security_opt: # enables full access to /sys and /proc, still far better than privileged: true
|
||||
- systempaths=unconfined
|
||||
- apparmor=unconfined
|
||||
group_add:
|
||||
- video
|
||||
devices:
|
||||
- /dev/rga:/dev/rga
|
||||
- /dev/dri:/dev/dri
|
||||
- /dev/dma_heap:/dev/dma_heap
|
||||
- /dev/mpp_service:/dev/mpp_service
|
||||
volumes:
|
||||
- /usr/bin/ffmpeg:/usr/bin/ffmpeg_mpp:ro
|
||||
- /lib/aarch64-linux-gnu:/lib/ffmpeg-mpp:ro
|
||||
- /lib/aarch64-linux-gnu/libblas.so.3:/lib/ffmpeg-mpp/libblas.so.3:ro # symlink is resolved by mounting
|
||||
- /lib/aarch64-linux-gnu/liblapack.so.3:/lib/ffmpeg-mpp/liblapack.so.3:ro # symlink is resolved by mounting
|
||||
- /lib/aarch64-linux-gnu/pulseaudio/libpulsecommon-15.99.so:/lib/ffmpeg-mpp/libpulsecommon-15.99.so:ro
|
23
docker/hwaccel.yml
Normal file
23
docker/hwaccel.yml
Normal file
|
@ -0,0 +1,23 @@
|
|||
version: "3.8"
|
||||
|
||||
# Hardware acceleration for transcoding - Optional
|
||||
# This is only needed if you want to use hardware acceleration for transcoding.
|
||||
# Depending on your hardware, you should uncomment the relevant lines below.
|
||||
|
||||
services:
|
||||
hwaccel:
|
||||
# devices:
|
||||
# - /dev/dri:/dev/dri # If using Intel QuickSync or VAAPI
|
||||
# volumes:
|
||||
# - /usr/lib/wsl:/usr/lib/wsl # If using VAAPI in WSL2
|
||||
# environment:
|
||||
# - NVIDIA_DRIVER_CAPABILITIES=all # If using NVIDIA GPU
|
||||
# - LD_LIBRARY_PATH=/usr/lib/wsl/lib # If using VAAPI in WSL2
|
||||
# - LIBVA_DRIVER_NAME=d3d12 # If using VAAPI in WSL2
|
||||
# deploy: # Uncomment this section if using NVIDIA GPU
|
||||
# resources:
|
||||
# reservations:
|
||||
# devices:
|
||||
# - driver: nvidia
|
||||
# count: 1
|
||||
# capabilities: [gpu,video]
|
21
docs/.gitignore
vendored
Normal file
21
docs/.gitignore
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Dependencies
|
||||
/node_modules
|
||||
|
||||
# Production
|
||||
/build
|
||||
|
||||
# Generated files
|
||||
.docusaurus
|
||||
.cache-loader
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
yarn.lock
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue