Compare commits
618 commits
230603-378
...
develop
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8990d48381 | ||
![]() |
9c6fae98d3 | ||
![]() |
55d5e7a732 | ||
![]() |
3507f5ef55 | ||
![]() |
3d962e2382 | ||
![]() |
0935d9fab0 | ||
![]() |
d36e5a3850 | ||
![]() |
fb4d99625d | ||
![]() |
cd014f02c5 | ||
![]() |
1d7655584c | ||
![]() |
13160c1640 | ||
![]() |
07d74e68f9 | ||
![]() |
37f647a8f9 | ||
![]() |
111fd0ebbe | ||
![]() |
7801e603f9 | ||
![]() |
4ffc548287 | ||
![]() |
200b0e1f1d | ||
![]() |
e4f287bbb8 | ||
![]() |
ab491c3032 | ||
![]() |
f48ff16ef5 | ||
![]() |
51178ea209 | ||
![]() |
5bc61b496f | ||
![]() |
eb63cb8324 | ||
![]() |
6ba87a52d8 | ||
![]() |
59456c57be | ||
![]() |
292eaf6ee0 | ||
![]() |
5d06770ab6 | ||
![]() |
63d071a219 | ||
![]() |
82c70772d2 | ||
![]() |
f714c1d759 | ||
![]() |
2d7a951c1a | ||
![]() |
eb19a4a893 | ||
![]() |
717e616700 | ||
![]() |
ac588057da | ||
![]() |
d32bc4cc6c | ||
![]() |
052fe82126 | ||
![]() |
8b5d547773 | ||
![]() |
e686b82cde | ||
![]() |
d06a182ac0 | ||
![]() |
be10bacb40 | ||
![]() |
0af4eefde1 | ||
![]() |
a2757c194b | ||
![]() |
79a60b8971 | ||
![]() |
797853d461 | ||
![]() |
b65e9137c6 | ||
![]() |
b5e6a126d8 | ||
![]() |
23a096b431 | ||
![]() |
29b9b6fc76 | ||
![]() |
1b41e89cbd | ||
![]() |
a41fb2c05a | ||
![]() |
a76a8bdb10 | ||
![]() |
adafb34433 | ||
![]() |
c9287f6f29 | ||
![]() |
c8d1b2e57b | ||
![]() |
380c521098 | ||
![]() |
bb6175f78c | ||
![]() |
b98e14f880 | ||
![]() |
1f112629ab | ||
![]() |
fdfbdc4ff7 | ||
![]() |
3a24d06a00 | ||
![]() |
4609edb6ee | ||
![]() |
36618c3a73 | ||
![]() |
539e18d984 | ||
![]() |
e97c96c509 | ||
![]() |
e85f349ea6 | ||
![]() |
602097635f | ||
![]() |
a247cc08f1 | ||
![]() |
c2de3aa0ec | ||
![]() |
d93e4c8ccd | ||
![]() |
4ffbbe9f84 | ||
![]() |
fe182d78f4 | ||
![]() |
7ec6b7e66f | ||
![]() |
8eee3597ef | ||
![]() |
4d2ff59a40 | ||
![]() |
5611678835 | ||
![]() |
b053dced4a | ||
![]() |
540c45c7c6 | ||
![]() |
47c6ee070c | ||
![]() |
afe3c13414 | ||
![]() |
e6b03f33f6 | ||
![]() |
47e9b20929 | ||
![]() |
227efd581d | ||
![]() |
d0ea838b37 | ||
![]() |
eae09353f2 | ||
![]() |
86f16790ea | ||
![]() |
29be637d62 | ||
![]() |
78e6dd88d8 | ||
![]() |
b07609c213 | ||
![]() |
948417f97e | ||
![]() |
6123ddf4af | ||
![]() |
34fa0306ba | ||
![]() |
597dd1e554 | ||
![]() |
e5e25e4e7e | ||
![]() |
f2cc300a06 | ||
![]() |
632c23ca47 | ||
![]() |
f28adc84e6 | ||
![]() |
ae585ba475 | ||
![]() |
4d568d79e9 | ||
![]() |
9b930a7910 | ||
![]() |
3ef8540018 | ||
![]() |
b88eccda0f | ||
![]() |
40bdbbf00f | ||
![]() |
46642b3ac0 | ||
![]() |
313948b103 | ||
![]() |
fcc3971394 | ||
![]() |
cdf9e76a13 | ||
![]() |
72da01722b | ||
![]() |
5c41ffb3ab | ||
![]() |
42a8a9f8b7 | ||
![]() |
0323c8ecd6 | ||
![]() |
dcbe798772 | ||
![]() |
b8fb87a9cf | ||
![]() |
a1aab20897 | ||
![]() |
c034f092c3 | ||
![]() |
a52ce4f9b1 | ||
![]() |
f76013c99e | ||
![]() |
19efee9b6d | ||
![]() |
5ca40cb94c | ||
![]() |
1e7b6c6251 | ||
![]() |
c40bf9a32a | ||
![]() |
8e3cb872ad | ||
![]() |
c962d1bcb2 | ||
![]() |
ef1a92ab80 | ||
![]() |
bc4d543d3f | ||
![]() |
50d6d6d44e | ||
![]() |
9abea5b55f | ||
![]() |
c2b56346b6 | ||
![]() |
09f8a58404 | ||
![]() |
95e1260234 | ||
![]() |
15377b1e98 | ||
![]() |
a6c64a4cb1 | ||
![]() |
0a3a24ea33 | ||
![]() |
b6fdbb6986 | ||
![]() |
67bd054f7b | ||
![]() |
60efc86649 | ||
![]() |
54f281a425 | ||
![]() |
a912383ede | ||
![]() |
7e7764807a | ||
![]() |
cca124e093 | ||
![]() |
132237e47b | ||
![]() |
f32d62cfe1 | ||
![]() |
29e4a5375b | ||
![]() |
ff3bcaff30 | ||
![]() |
67562d9d90 | ||
![]() |
027aca2994 | ||
![]() |
8524fcc149 | ||
![]() |
326ea3475a | ||
![]() |
67b415dbc7 | ||
![]() |
afb01a8515 | ||
![]() |
2570b2d482 | ||
![]() |
cecff7374d | ||
![]() |
4a814f05d5 | ||
![]() |
5465324693 | ||
![]() |
6d0f3db196 | ||
![]() |
102348549d | ||
![]() |
029f57d1e6 | ||
![]() |
2fed4b549c | ||
![]() |
05e29170bf | ||
![]() |
b67043955d | ||
![]() |
c8287e462f | ||
![]() |
0a03071982 | ||
![]() |
d71beeb903 | ||
![]() |
fd924f3b4c | ||
![]() |
afd77a6a30 | ||
![]() |
c5dc0af24c | ||
![]() |
8dc222d584 | ||
![]() |
89bbba06ce | ||
![]() |
ef402ea2e7 | ||
![]() |
297b3adcc2 | ||
![]() |
01cb95ae74 | ||
![]() |
bebf45a246 | ||
![]() |
17b473d867 | ||
![]() |
170c6bb3dc | ||
![]() |
2e70152f9f | ||
![]() |
c86bbdce0c | ||
![]() |
0a44f4f528 | ||
![]() |
b7bbc59fca | ||
![]() |
544eea50f2 | ||
![]() |
09ad17d10a | ||
![]() |
57b1bb4b7d | ||
![]() |
d28b672796 | ||
![]() |
8975c781c5 | ||
![]() |
6a452bcf43 | ||
![]() |
bac10302da | ||
![]() |
63f708417d | ||
![]() |
c6f462488b | ||
![]() |
666e6c6a6c | ||
![]() |
f692380dc6 | ||
![]() |
22aee3b6c0 | ||
![]() |
f8efcdbd36 | ||
![]() |
a470b908a4 | ||
![]() |
aa9e598392 | ||
![]() |
ae5d8ae6d4 | ||
![]() |
2f8a8b6ab7 | ||
![]() |
f087658ed0 | ||
![]() |
2f9792e541 | ||
![]() |
f4dfc02a85 | ||
![]() |
beb6d409ff | ||
![]() |
0470899016 | ||
![]() |
3d1f93f94e | ||
![]() |
c74ad81f77 | ||
![]() |
4d8bf12c0b | ||
![]() |
3c0f1f37ad | ||
![]() |
e6b1a28bcf | ||
![]() |
eb44d637f3 | ||
![]() |
fb419e26b7 | ||
![]() |
80dd926f2d | ||
![]() |
0f321b10bc | ||
![]() |
ede4b3b897 | ||
![]() |
f3fb28bdc2 | ||
![]() |
98eba527eb | ||
![]() |
dd87d2a6f8 | ||
![]() |
10a58f2097 | ||
![]() |
57c2aad29a | ||
![]() |
61e7de69a4 | ||
![]() |
63515f84d7 | ||
![]() |
b0ef310b96 | ||
![]() |
c78d12b56d | ||
![]() |
9b3efe6872 | ||
![]() |
604849e92c | ||
![]() |
b45b20aa53 | ||
![]() |
1e11d8986e | ||
![]() |
eb1a0150a3 | ||
![]() |
478bf67eb6 | ||
![]() |
df8fa5693d | ||
![]() |
8d7afaa476 | ||
![]() |
7106b25da1 | ||
![]() |
0e26ffb064 | ||
![]() |
a0904c4008 | ||
![]() |
4453d0ca8d | ||
![]() |
59ddf7e310 | ||
![]() |
fd19bc0a5c | ||
![]() |
f0ee301b28 | ||
![]() |
ffd2dd41a8 | ||
![]() |
7354ea20a9 | ||
![]() |
00c2dfc9f3 | ||
![]() |
ab8c71cb66 | ||
![]() |
12ae011ba9 | ||
![]() |
7c86148d0a | ||
![]() |
dca3258112 | ||
![]() |
20d50e62b8 | ||
![]() |
37a2392d70 | ||
![]() |
71aed413e9 | ||
![]() |
d3aea39a26 | ||
![]() |
5e0ad1f4fd | ||
![]() |
d843cf0783 | ||
![]() |
2f38402d9c | ||
![]() |
edc408369a | ||
![]() |
8c6ce1f81b | ||
![]() |
ab3262a634 | ||
![]() |
cfe1ff3299 | ||
![]() |
9cfd9c0b66 | ||
![]() |
654a9b4130 | ||
![]() |
26ca084699 | ||
![]() |
20407452f0 | ||
![]() |
e59851350b | ||
![]() |
c38ab232fc | ||
![]() |
8b6de380d0 | ||
![]() |
63e672a5c9 | ||
![]() |
50abd9b632 | ||
![]() |
9e1d9702ae | ||
![]() |
e5166f7120 | ||
![]() |
2339197311 | ||
![]() |
04c19dd282 | ||
![]() |
529103462c | ||
![]() |
31a2cff5b6 | ||
![]() |
4ece10b5af | ||
![]() |
3788a82d91 | ||
![]() |
1fab43f84b | ||
![]() |
b378d14b80 | ||
![]() |
7f24c9a4b4 | ||
![]() |
bf9b21fef8 | ||
![]() |
5b912bb123 | ||
![]() |
cf0da6327f | ||
![]() |
a865300666 | ||
![]() |
0f1106eb3b | ||
![]() |
2843342d00 | ||
![]() |
ce4cb6bd47 | ||
![]() |
1aff53ed3e | ||
![]() |
9c30d1e789 | ||
![]() |
d9a9db9c71 | ||
![]() |
1c6b100b1f | ||
![]() |
7906dd8ce7 | ||
![]() |
83500af344 | ||
![]() |
267430bf78 | ||
![]() |
33bf0cede6 | ||
![]() |
d7e21f449f | ||
![]() |
5e2f07ab56 | ||
![]() |
c7d054960e | ||
![]() |
295fe0e6fa | ||
![]() |
c2e38f942b | ||
![]() |
8957462672 | ||
![]() |
4d1003846c | ||
![]() |
d5a1526291 | ||
![]() |
58b66e3954 | ||
![]() |
dfd706b405 | ||
![]() |
8dbbe1c4f9 | ||
![]() |
90e72bb486 | ||
![]() |
61a9ad0a19 | ||
![]() |
06d8816a7f | ||
![]() |
ca20be57ce | ||
![]() |
67472eccfe | ||
![]() |
dcbb0cbfe9 | ||
![]() |
6e282cc6b3 | ||
![]() |
85506f9373 | ||
![]() |
b65362b35a | ||
![]() |
43655ba591 | ||
![]() |
9eb12c7183 | ||
![]() |
cfd6149a6d | ||
![]() |
a92653d4f2 | ||
![]() |
405d56237e | ||
![]() |
9c21f31db1 | ||
![]() |
5bc504491a | ||
![]() |
728af5d8dc | ||
![]() |
3299ace3e6 | ||
![]() |
edc97a68ad | ||
![]() |
139562e393 | ||
![]() |
09e09f4d23 | ||
![]() |
b93baa8ed0 | ||
![]() |
9fa7563f0c | ||
![]() |
2bc8e8cc18 | ||
![]() |
e737083822 | ||
![]() |
a39fed36e7 | ||
![]() |
27a551bbd2 | ||
![]() |
c7d361d61a | ||
![]() |
c709567ee7 | ||
![]() |
e1776f7ed5 | ||
![]() |
cf364ac27a | ||
![]() |
3cb4b69dac | ||
![]() |
001466b9fa | ||
![]() |
59207846bd | ||
![]() |
f12c558f67 | ||
![]() |
ee97436539 | ||
![]() |
586c224d15 | ||
![]() |
5ae21dcddb | ||
![]() |
a8595066e4 | ||
![]() |
096b306420 | ||
![]() |
311288312e | ||
![]() |
7cb1dcbd22 | ||
![]() |
ab61322365 | ||
![]() |
2159038c73 | ||
![]() |
18efd74aab | ||
![]() |
304c8e3ae6 | ||
![]() |
202da83f04 | ||
![]() |
516eec932e | ||
![]() |
63695ab801 | ||
![]() |
12608a7a31 | ||
![]() |
dbcaf775a8 | ||
![]() |
71986e1565 | ||
![]() |
0ce71a9b80 | ||
![]() |
a30cbb19b7 | ||
![]() |
6463fe03be | ||
![]() |
3c141ddf20 | ||
![]() |
d575807929 | ||
![]() |
207a99fab4 | ||
![]() |
132284eb0f | ||
![]() |
4154649c76 | ||
![]() |
7197d6b651 | ||
![]() |
5d26bada42 | ||
![]() |
dd11d68236 | ||
![]() |
806f1be526 | ||
![]() |
0890c547af | ||
![]() |
93408db052 | ||
![]() |
fa3d3c326c | ||
![]() |
36119a5011 | ||
![]() |
adf19db33b | ||
![]() |
4d47a5bef5 | ||
![]() |
c0ee909971 | ||
![]() |
0e4ce95ff1 | ||
![]() |
ec1e3f0fb6 | ||
![]() |
b92dd1af99 | ||
![]() |
386bf823d7 | ||
![]() |
a59885cede | ||
![]() |
6fbf73a8bd | ||
![]() |
ff4b8c8cf6 | ||
![]() |
20d20c7fa9 | ||
![]() |
afe2190797 | ||
![]() |
ba612f6619 | ||
![]() |
4fffc8e2d6 | ||
![]() |
27e8537640 | ||
![]() |
fd55b9046c | ||
![]() |
44759d6673 | ||
![]() |
1507525ba4 | ||
![]() |
2e367870a9 | ||
![]() |
5c7f8d1726 | ||
![]() |
51fc883459 | ||
![]() |
dc4eca6fd2 | ||
![]() |
e4346adf13 | ||
![]() |
7020389a75 | ||
![]() |
e30c2e5f1b | ||
![]() |
fbd6ad2a95 | ||
![]() |
80c10bb35c | ||
![]() |
a3f27c8d67 | ||
![]() |
f15ca1d7e5 | ||
![]() |
3c1b673f17 | ||
![]() |
d7f70c2a0c | ||
![]() |
c6bb28f60d | ||
![]() |
69f15924ab | ||
![]() |
32c0d0ae66 | ||
![]() |
fc7269480c | ||
![]() |
5deedbf47c | ||
![]() |
736d1a9090 | ||
![]() |
9b282e4ce0 | ||
![]() |
ae52117557 | ||
![]() |
09f324a1d9 | ||
![]() |
a0f16b60b5 | ||
![]() |
c7dae2383f | ||
![]() |
730cbeaf2c | ||
![]() |
b85c2a2dab | ||
![]() |
874371b046 | ||
![]() |
f1c7378fbf | ||
![]() |
be0fdc1774 | ||
![]() |
cb9d601dfa | ||
![]() |
5b4b57e3bf | ||
![]() |
91cc358fc5 | ||
![]() |
3212724aae | ||
![]() |
3581aafc29 | ||
![]() |
3f6cb27497 | ||
![]() |
5822a26b8c | ||
![]() |
8ed9f3f409 | ||
![]() |
a287830d1f | ||
![]() |
5da5ee72a7 | ||
![]() |
7f13218229 | ||
![]() |
cc7c3e3a64 | ||
![]() |
11beec835c | ||
![]() |
cb66022925 | ||
![]() |
c82a4b2287 | ||
![]() |
68fea5954d | ||
![]() |
2bf50082f5 | ||
![]() |
77b5996640 | ||
![]() |
71c30e4218 | ||
![]() |
34066da2de | ||
![]() |
104f87ea6d | ||
![]() |
7d8325ef6b | ||
![]() |
4e05270385 | ||
![]() |
e6679e6c50 | ||
![]() |
b194188e91 | ||
![]() |
26ad621bd5 | ||
![]() |
b32e3cd244 | ||
![]() |
f9ea868582 | ||
![]() |
470694e774 | ||
![]() |
16c78767b2 | ||
![]() |
eece27a0ae | ||
![]() |
9f17d64ea3 | ||
![]() |
54326361b5 | ||
![]() |
eecce8c743 | ||
![]() |
2a83345d11 | ||
![]() |
65a5dd2cb4 | ||
![]() |
a3f253c9de | ||
![]() |
a3bb4a2f0e | ||
![]() |
63daf10bf9 | ||
![]() |
5ba00e74e5 | ||
![]() |
ca40954796 | ||
![]() |
82dac4b7db | ||
![]() |
3c7b1b29a7 | ||
![]() |
098d39f9dc | ||
![]() |
a58318f304 | ||
![]() |
31578a3806 | ||
![]() |
36bac7ab48 | ||
![]() |
83473f6f93 | ||
![]() |
098255c741 | ||
![]() |
40b1f1f5cd | ||
![]() |
bc18507a14 | ||
![]() |
ec09145fef | ||
![]() |
348442d355 | ||
![]() |
6b8a75f29f | ||
![]() |
b59b954d1f | ||
![]() |
2c33a34ac3 | ||
![]() |
6c1df15f34 | ||
![]() |
1df12bd5c9 | ||
![]() |
ff25b5f755 | ||
![]() |
1fcef1bec4 | ||
![]() |
97e9ad9c1e | ||
![]() |
8f165c653f | ||
![]() |
fd0ef7825e | ||
![]() |
c9d1413ddb | ||
![]() |
ef1487ddd2 | ||
![]() |
22bb6634af | ||
![]() |
33b08f15e5 | ||
![]() |
69d302248c | ||
![]() |
60d280430e | ||
![]() |
ea8ee0938c | ||
![]() |
2a002eca78 | ||
![]() |
6b9aeda48f | ||
![]() |
ad3da85ecb | ||
![]() |
90eac1966b | ||
![]() |
bf0ad09557 | ||
![]() |
ce13135743 | ||
![]() |
5ca3329e2d | ||
![]() |
062495b6f2 | ||
![]() |
20df14e9d1 | ||
![]() |
b5b13b0fbb | ||
![]() |
4727f769d2 | ||
![]() |
0817f1876d | ||
![]() |
e1364f288c | ||
![]() |
74f6954a90 | ||
![]() |
7c70ac0126 | ||
![]() |
3d828a7df0 | ||
![]() |
84a4c3c57e | ||
![]() |
70ad541c01 | ||
![]() |
68b40babfa | ||
![]() |
0d243d5ea4 | ||
![]() |
73fa7bbe86 | ||
![]() |
3e2f215786 | ||
![]() |
63cebc7409 | ||
![]() |
4931889b5e | ||
![]() |
08070978cf | ||
![]() |
3b7b551cca | ||
![]() |
19b4490232 | ||
![]() |
7b9b2ae0c6 | ||
![]() |
44603857fa | ||
![]() |
3cf1c699df | ||
![]() |
8c0955dd41 | ||
![]() |
101d8d7baf | ||
![]() |
cc5ff21b4a | ||
![]() |
5e645b9bfd | ||
![]() |
47e619a828 | ||
![]() |
7dc02e4288 | ||
![]() |
651782eb4d | ||
![]() |
5fb9e95217 | ||
![]() |
0e93bd8aa2 | ||
![]() |
781bb0b04f | ||
![]() |
40a018532a | ||
![]() |
f225191659 | ||
![]() |
7e680a67cb | ||
![]() |
d574abff33 | ||
![]() |
00acfb5e45 | ||
![]() |
5176380a56 | ||
![]() |
bc0593637a | ||
![]() |
131ccbab15 | ||
![]() |
89842c8442 | ||
![]() |
c172e06c04 | ||
![]() |
09bd901272 | ||
![]() |
38e8e1b7ca | ||
![]() |
b9a7cb4fec | ||
![]() |
14b415dcba | ||
![]() |
945737b01e | ||
![]() |
dbc2887447 | ||
![]() |
91c006a63c | ||
![]() |
bd27ad08e7 | ||
![]() |
ff5f3ddeb1 | ||
![]() |
c3fd96adf7 | ||
![]() |
11e7d3f0d1 | ||
![]() |
e5db48b300 | ||
![]() |
5041b7f211 | ||
![]() |
2f21b4fec5 | ||
![]() |
1c9dad9cdd | ||
![]() |
da8e5a9af0 | ||
![]() |
9a699f234d | ||
![]() |
da61515c4a | ||
![]() |
03f92bc0db | ||
![]() |
19ac83dd20 | ||
![]() |
2dc9e142ef | ||
![]() |
6ebbab9b17 | ||
![]() |
8e43186454 | ||
![]() |
a6d3eee331 | ||
![]() |
9b3917dd00 | ||
![]() |
19699c49c0 | ||
![]() |
83d10ea00e | ||
![]() |
08ba1e1c05 | ||
![]() |
37c40c9b3d | ||
![]() |
7910994166 | ||
![]() |
111d0a2ae6 | ||
![]() |
b4dffec0a3 | ||
![]() |
5f103681fb | ||
![]() |
29462bce13 | ||
![]() |
e1ddaa7c0b | ||
![]() |
f53619b004 | ||
![]() |
1e55e5b1d9 | ||
![]() |
186d1f26e7 | ||
![]() |
43e98cc687 | ||
![]() |
accb17bec9 | ||
![]() |
17242fb079 | ||
![]() |
749d4b6d7e | ||
![]() |
bae5eace8f | ||
![]() |
0818d5995c | ||
![]() |
96e0981c31 | ||
![]() |
ee6e6c66e3 | ||
![]() |
31f9b88495 | ||
![]() |
87b6d72477 | ||
![]() |
054a0764c5 | ||
![]() |
df0d93b1e4 | ||
![]() |
fa5d8b42bc | ||
![]() |
ae095f465f | ||
![]() |
f1e98f9915 | ||
![]() |
90a18f6e7d | ||
![]() |
1776728570 | ||
![]() |
4a66c7551d | ||
![]() |
f933aaa9ff | ||
![]() |
a7c8f0102f | ||
![]() |
4430c7953e | ||
![]() |
b91723e90c | ||
![]() |
a7cc1e367f | ||
![]() |
53aeaffe0e | ||
![]() |
9e316a2ec0 | ||
![]() |
9a5af3176e | ||
![]() |
00b1472419 | ||
![]() |
edf53a921a | ||
![]() |
4ecece557d | ||
![]() |
6a4bfa84be | ||
![]() |
0b9cb2e1fb | ||
![]() |
97ae47b06e | ||
![]() |
092fe04b2d | ||
![]() |
39e26b7f0e | ||
![]() |
a891da7dec | ||
![]() |
9e086c7ebd | ||
![]() |
70dbf1d040 | ||
![]() |
500ada9615 | ||
![]() |
9068efc697 | ||
![]() |
573c172457 | ||
![]() |
d92613c435 | ||
![]() |
66a38e8f44 | ||
![]() |
83ce69b1d2 | ||
![]() |
59bf7cb9bd | ||
![]() |
dda00ba239 | ||
![]() |
a45f8d40cf | ||
![]() |
db454641cf | ||
![]() |
86d05eed4f |
638 changed files with 48096 additions and 25650 deletions
|
@ -2,7 +2,7 @@
|
|||
|
||||
**By using the software and services we provide, you agree to our [Terms of Service](https://www.photoprism.app/terms), including our [Privacy Policy](https://www.photoprism.app/privacy) and the following Code of Conduct. It explains the "dos and don’ts" when interacting with our team and other community members.**
|
||||
|
||||
*Last Updated: May 13, 2023*
|
||||
*Last Updated: July 5, 2023*
|
||||
|
||||
## Rules
|
||||
|
||||
|
@ -18,7 +18,7 @@ Because we want our Code of Conduct to be easy to understand and implement, we h
|
|||
|
||||
Not everyone has experience with Open Source communities and intuitively knows what is acceptable. In that case, the following guidelines and examples are meant to provide a quick overview and help you avoid the most common pitfalls:
|
||||
|
||||
(a) Do not [feel entitled](https://www.reddit.com/r/photoprism/comments/13emwf0/did_you_guys_really_nerf_hardware_transcoding/) to free software, support, or advice, especially if you are not a contributor, [member](https://link.photoprism.app/membership), or paying customer. Don't expect contributors to report to you and [meet deadlines](https://docs.photoprism.app/developer-guide/code-quality/#go-slow-before-you-go-fast) as if they work for you or owe you something, even if you donated a small amount. We also ask that you do not use GitHub Issues or other development tools to start general discussions, get technical support, or express personal opinions.
|
||||
(a) Do not [feel entitled](https://www.reddit.com/r/photoprism/comments/13emwf0/did_you_guys_really_nerf_hardware_transcoding/) to free software, support, or advice, especially if you are not a contributor, [member](https://link.photoprism.app/membership), or business customer. Don't expect contributors to [give status reports](https://docs.photoprism.app/developer-guide/code-quality/#go-slow-before-you-go-fast) as if they work for you or owe you something, even if you have donated a small amount. We also ask that you do not use GitHub Issues or other development tools to start general discussions, get technical support, or express personal opinions.
|
||||
|
||||
(b) Honor **Rule #2**, [read the docs](https://docs.photoprism.app) and [determine the cause of your problem](https://docs.photoprism.app/getting-started/troubleshooting/) before opening invalid bug reports, starting a public "shitstorm", or insulting other community members in our chat rooms. Aside from being annoying for everyone, it also keeps our team from working on features and enhancements that users like you are waiting for.
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ Feel free to [contact us](https://www.photoprism.app/contact) with anything that
|
|||
You can also contribute by…
|
||||
|
||||
* answering questions in the [Community Chat](https://link.photoprism.app/chat), on [Reddit](https://link.photoprism.app/reddit) and in [GitHub Discussions](https://link.photoprism.app/discussions)
|
||||
* helping us [translate](https://docs.photoprism.app/developer-guide/translations-weblate/) the Web UI
|
||||
* helping us [translate](https://docs.photoprism.app/developer-guide/translations-weblate/) the Web UI on [Weblate](https://translate.photoprism.app/)
|
||||
* [conducting research](https://github.com/photoprism/photoprism/issues?q=is%3Aopen+is%3Aissue+label%3Aresearch) and [improving the documentation](https://github.com/photoprism/photoprism/issues?q=is%3Aopen+is%3Aissue+label%3Adocs)
|
||||
* publishing tutorials, blog posts, and podcasts
|
||||
* voting for us on pages like:
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
# Ubuntu 23.04 (Lunar Lobster)
|
||||
FROM photoprism/develop:230516-lunar
|
||||
# Ubuntu 23.10 (Mantic Minotaur)
|
||||
FROM photoprism/develop:231206-mantic
|
||||
|
||||
## Alternative Environments:
|
||||
# FROM photoprism/develop:armv7 # ARMv7 (32bit)
|
||||
# FROM photoprism/develop:lunar # Ubuntu 23.04 (Lunar Lobster)
|
||||
# FROM photoprism/develop:jammy # Ubuntu 22.04 LTS (Jammy Jellyfish)
|
||||
# FROM photoprism/develop:impish # Ubuntu 21.10 (Impish Indri)
|
||||
# FROM photoprism/develop:bookworm # Debian 12 (Bookworm)
|
||||
|
@ -13,4 +15,4 @@ WORKDIR "/go/src/github.com/photoprism/photoprism"
|
|||
|
||||
# Copy source to image.
|
||||
COPY . .
|
||||
COPY --chown=root:root /scripts/dist/ /scripts/
|
||||
COPY --chown=root:root /scripts/dist/ /scripts/
|
||||
|
|
120
Makefile
120
Makefile
|
@ -22,7 +22,7 @@ BUILD_ARCH ?= $(shell scripts/dist/arch.sh)
|
|||
JS_BUILD_PATH ?= $(shell realpath "./assets/static/build")
|
||||
|
||||
# Install parameters.
|
||||
INSTALL_PATH ?= $(BUILD_PATH)/photoprism-$(BUILD_TAG)-$(shell echo $(BUILD_OS) | tr '[:upper:]' '[:lower:]')-$(BUILD_ARCH)
|
||||
INSTALL_PATH ?= $(BUILD_PATH)/photoprism-ce_$(BUILD_TAG)-$(shell echo $(BUILD_OS) | tr '[:upper:]' '[:lower:]')-$(BUILD_ARCH)
|
||||
DESTDIR ?= $(INSTALL_PATH)
|
||||
DESTUID ?= 1000
|
||||
DESTGID ?= 1000
|
||||
|
@ -54,6 +54,7 @@ all: dep build-js
|
|||
dep: dep-tensorflow dep-js
|
||||
biuld: build
|
||||
build: build-go
|
||||
build-all: build-go build-js
|
||||
pull: docker-pull
|
||||
test: test-js test-go
|
||||
test-go: reset-sqlite run-test-go
|
||||
|
@ -107,19 +108,23 @@ clean:
|
|||
[ ! -d "$(JS_BUILD_PATH)" ] || rm -rf --preserve-root $(JS_BUILD_PATH)
|
||||
tar.gz:
|
||||
$(info Creating tar.gz archives from the directories in "$(BUILD_PATH)"...)
|
||||
find "$(BUILD_PATH)" -maxdepth 1 -mindepth 1 -type d -exec tar --exclude='.[^/]*' -C {} -czf {}.tar.gz . \;
|
||||
find "$(BUILD_PATH)" -maxdepth 1 -mindepth 1 -type d -name "photoprism*" -exec tar --exclude='.[^/]*' -C {} -czf {}.tar.gz . \;
|
||||
pkg: pkg-amd64 pkg-arm64
|
||||
pkg-amd64:
|
||||
docker run --rm -u $(UID) --platform=amd64 --pull=always -v ".:/go/src/github.com/photoprism/photoprism" --entrypoint "" photoprism/develop:jammy make all install tar.gz
|
||||
pkg-arm64:
|
||||
docker run --rm -u $(UID) --platform=arm64 --pull=always -v ".:/go/src/github.com/photoprism/photoprism" --entrypoint "" photoprism/develop:jammy make all install tar.gz
|
||||
install:
|
||||
$(info Installing in "$(DESTDIR)"...)
|
||||
@[ ! -d "$(DESTDIR)" ] || (echo "ERROR: Install path '$(DESTDIR)' already exists!"; exit 1)
|
||||
mkdir --mode=$(INSTALL_MODE) -p $(DESTDIR)
|
||||
env TMPDIR="$(BUILD_PATH)" ./scripts/dist/install-tensorflow.sh $(DESTDIR)
|
||||
rm -rf --preserve-root $(DESTDIR)/include
|
||||
(cd $(DESTDIR) && mkdir -p bin lib assets config config/examples)
|
||||
(cd $(DESTDIR) && mkdir -p bin lib assets)
|
||||
./scripts/build.sh prod "$(DESTDIR)/bin/$(BINARY_NAME)"
|
||||
rsync -r -l --safe-links --exclude-from=assets/.buildignore --chmod=a+r,u+rw ./assets/ $(DESTDIR)/assets
|
||||
wget -O $(DESTDIR)/assets/static/img/wallpaper/welcome.jpg https://cdn.photoprism.app/wallpaper/welcome.jpg
|
||||
wget -O $(DESTDIR)/assets/static/img/preview.jpg https://cdn.photoprism.app/img/preview.jpg
|
||||
cp internal/config/testdata/*.yml $(DESTDIR)/config/examples
|
||||
chown -R $(INSTALL_USER) $(DESTDIR)
|
||||
chmod -R $(INSTALL_MODE) $(DESTDIR)
|
||||
chmod -R $(INSTALL_MODE_BIN) $(DESTDIR)/bin $(DESTDIR)/lib
|
||||
|
@ -141,9 +146,9 @@ acceptance-sqlite-restart:
|
|||
rm -rf storage/acceptance/originals/2011
|
||||
rm -rf storage/acceptance/originals/2013
|
||||
rm -rf storage/acceptance/originals/2017
|
||||
./photoprism -p -c "./storage/acceptance/config-sqlite" --test start -d
|
||||
./photoprism --auth-mode="public" -c "./storage/acceptance/config-sqlite" --test start -d
|
||||
acceptance-sqlite-stop:
|
||||
./photoprism -p -c "./storage/acceptance/config-sqlite" --test stop
|
||||
./photoprism --auth-mode="public" -c "./storage/acceptance/config-sqlite" --test stop
|
||||
acceptance-auth-sqlite-restart:
|
||||
cp -f storage/acceptance/backup.db storage/acceptance/index.db
|
||||
cp -f storage/acceptance/config-sqlite/settingsBackup.yml storage/acceptance/config-sqlite/settings.yml
|
||||
|
@ -316,7 +321,7 @@ docker-develop: docker-develop-latest
|
|||
docker-develop-all: docker-develop-latest docker-develop-other
|
||||
docker-develop-latest: docker-develop-ubuntu
|
||||
docker-develop-debian: docker-develop-bookworm docker-develop-bookworm-slim
|
||||
docker-develop-ubuntu: docker-develop-lunar docker-develop-lunar-slim
|
||||
docker-develop-ubuntu: docker-develop-mantic docker-develop-mantic-slim
|
||||
docker-develop-other: docker-develop-debian docker-develop-bullseye docker-develop-bullseye-slim docker-develop-buster
|
||||
docker-develop-bookworm:
|
||||
docker pull --platform=amd64 debian:bookworm-slim
|
||||
|
@ -334,8 +339,9 @@ docker-develop-bullseye-slim:
|
|||
docker pull --platform=amd64 debian:bullseye-slim
|
||||
docker pull --platform=arm64 debian:bullseye-slim
|
||||
scripts/docker/buildx-multi.sh develop linux/amd64,linux/arm64 bullseye-slim /bullseye-slim
|
||||
develop-armv7: docker-develop-armv7
|
||||
docker-develop-armv7:
|
||||
docker pull --platform=arm ubuntu:jammy
|
||||
docker pull --platform=arm ubuntu:mantic
|
||||
scripts/docker/buildx.sh develop linux/arm armv7 /armv7
|
||||
docker-develop-buster:
|
||||
docker pull --platform=amd64 golang:1-buster
|
||||
|
@ -356,13 +362,21 @@ docker-develop-jammy-slim:
|
|||
docker-develop-lunar:
|
||||
docker pull --platform=amd64 ubuntu:lunar
|
||||
docker pull --platform=arm64 ubuntu:lunar
|
||||
scripts/docker/buildx-multi.sh develop linux/amd64,linux/arm64 lunar /lunar "-t photoprism/develop:latest -t photoprism/develop:ubuntu"
|
||||
scripts/docker/buildx-multi.sh develop linux/amd64,linux/arm64 lunar /lunar
|
||||
docker-develop-lunar-slim:
|
||||
docker pull --platform=amd64 ubuntu:lunar
|
||||
docker pull --platform=arm64 ubuntu:lunar
|
||||
scripts/docker/buildx-multi.sh develop linux/amd64,linux/arm64 lunar-slim /lunar-slim
|
||||
docker-develop-mantic:
|
||||
docker pull --platform=amd64 ubuntu:mantic
|
||||
docker pull --platform=arm64 ubuntu:mantic
|
||||
scripts/docker/buildx-multi.sh develop linux/amd64,linux/arm64 mantic /mantic "-t photoprism/develop:latest -t photoprism/develop:ubuntu"
|
||||
docker-develop-mantic-slim:
|
||||
docker pull --platform=amd64 ubuntu:mantic
|
||||
docker pull --platform=arm64 ubuntu:mantic
|
||||
scripts/docker/buildx-multi.sh develop linux/amd64,linux/arm64 mantic-slim /mantic-slim
|
||||
unstable: docker-unstable
|
||||
docker-unstable: docker-unstable-lunar
|
||||
docker-unstable: docker-unstable-mantic
|
||||
docker-unstable-jammy:
|
||||
docker pull --platform=amd64 photoprism/develop:jammy
|
||||
docker pull --platform=amd64 photoprism/develop:jammy-slim
|
||||
|
@ -371,12 +385,17 @@ docker-unstable-lunar:
|
|||
docker pull --platform=amd64 photoprism/develop:lunar
|
||||
docker pull --platform=amd64 photoprism/develop:lunar-slim
|
||||
scripts/docker/buildx-multi.sh photoprism linux/amd64 unstable-ce /lunar
|
||||
preview: docker-preview
|
||||
docker-preview: docker-preview-latest
|
||||
docker-unstable-mantic:
|
||||
docker pull --platform=amd64 photoprism/develop:mantic
|
||||
docker pull --platform=amd64 photoprism/develop:mantic-slim
|
||||
scripts/docker/buildx-multi.sh photoprism linux/amd64 unstable-ce /mantic
|
||||
preview: docker-preview-ce
|
||||
docker-preview: docker-preview-ce
|
||||
docker-preview-all: docker-preview-latest docker-preview-other
|
||||
docker-preview-ce: docker-preview-mantic
|
||||
docker-preview-latest: docker-preview-ubuntu
|
||||
docker-preview-debian: docker-preview-bookworm
|
||||
docker-preview-ubuntu: docker-preview-lunar
|
||||
docker-preview-ubuntu: docker-preview-mantic
|
||||
docker-preview-other: docker-preview-debian docker-preview-bullseye
|
||||
docker-preview-arm: docker-preview-arm64 docker-preview-armv7
|
||||
docker-preview-bookworm:
|
||||
|
@ -387,7 +406,7 @@ docker-preview-bookworm:
|
|||
scripts/docker/buildx-multi.sh photoprism linux/amd64,linux/arm64 preview-bookworm /bookworm "-t photoprism/photoprism:preview-ce-debian"
|
||||
docker-preview-armv7:
|
||||
docker pull --platform=arm photoprism/develop:armv7
|
||||
docker pull --platform=arm ubuntu:jammy
|
||||
docker pull --platform=arm ubuntu:mantic
|
||||
scripts/docker/buildx.sh photoprism linux/arm preview-armv7 /armv7
|
||||
docker-preview-arm64:
|
||||
docker pull --platform=arm64 photoprism/develop:lunar
|
||||
|
@ -405,6 +424,12 @@ docker-preview-buster:
|
|||
docker pull --platform=amd64 debian:buster-slim
|
||||
docker pull --platform=arm64 debian:buster-slim
|
||||
scripts/docker/buildx-multi.sh photoprism linux/amd64,linux/arm64 preview-buster /buster
|
||||
docker-preview-impish:
|
||||
docker pull --platform=amd64 photoprism/develop:impish
|
||||
docker pull --platform=arm64 photoprism/develop:impish
|
||||
docker pull --platform=amd64 ubuntu:impish
|
||||
docker pull --platform=arm64 ubuntu:impish
|
||||
scripts/docker/buildx-multi.sh photoprism linux/amd64,linux/arm64 preview-impish /impish
|
||||
docker-preview-jammy:
|
||||
docker pull --platform=amd64 photoprism/develop:jammy
|
||||
docker pull --platform=amd64 photoprism/develop:jammy-slim
|
||||
|
@ -416,19 +441,19 @@ docker-preview-lunar:
|
|||
docker pull --platform=amd64 photoprism/develop:lunar-slim
|
||||
docker pull --platform=arm64 photoprism/develop:lunar
|
||||
docker pull --platform=arm64 photoprism/develop:lunar-slim
|
||||
scripts/docker/buildx-multi.sh photoprism linux/amd64,linux/arm64 preview-ce /lunar "-t photoprism/photoprism:preview-ce-ubuntu"
|
||||
docker-preview-impish:
|
||||
docker pull --platform=amd64 photoprism/develop:impish
|
||||
docker pull --platform=arm64 photoprism/develop:impish
|
||||
docker pull --platform=amd64 ubuntu:impish
|
||||
docker pull --platform=arm64 ubuntu:impish
|
||||
scripts/docker/buildx-multi.sh photoprism linux/amd64,linux/arm64 preview-impish /impish
|
||||
release: docker-release-all
|
||||
scripts/docker/buildx-multi.sh photoprism linux/amd64,linux/arm64 preview-ce /lunar
|
||||
docker-preview-mantic:
|
||||
docker pull --platform=amd64 photoprism/develop:mantic
|
||||
docker pull --platform=amd64 photoprism/develop:mantic-slim
|
||||
docker pull --platform=arm64 photoprism/develop:mantic
|
||||
docker pull --platform=arm64 photoprism/develop:mantic-slim
|
||||
scripts/docker/buildx-multi.sh photoprism linux/amd64,linux/arm64 preview-ce /mantic
|
||||
release: docker-release
|
||||
docker-release: docker-release-latest
|
||||
docker-release-all: docker-release-latest docker-release-other
|
||||
docker-release-latest: docker-release-ubuntu
|
||||
docker-release-debian: docker-release-bookworm
|
||||
docker-release-ubuntu: docker-release-lunar
|
||||
docker-release-ubuntu: docker-release-mantic
|
||||
docker-release-other: docker-release-debian docker-release-bullseye
|
||||
docker-release-arm: docker-release-arm64 docker-release-armv7
|
||||
docker-release-bookworm:
|
||||
|
@ -439,7 +464,7 @@ docker-release-bookworm:
|
|||
scripts/docker/buildx-multi.sh photoprism linux/amd64,linux/arm64 ce-bookworm /bookworm "-t photoprism/photoprism:ce-debian"
|
||||
docker-release-armv7:
|
||||
docker pull --platform=arm photoprism/develop:armv7
|
||||
docker pull --platform=arm ubuntu:jammy
|
||||
docker pull --platform=arm ubuntu:mantic
|
||||
scripts/docker/buildx.sh photoprism linux/arm armv7 /armv7
|
||||
docker-release-arm64:
|
||||
docker pull --platform=arm64 photoprism/develop:lunar
|
||||
|
@ -457,6 +482,12 @@ docker-release-buster:
|
|||
docker pull --platform=amd64 debian:buster-slim
|
||||
docker pull --platform=arm64 debian:buster-slim
|
||||
scripts/docker/buildx-multi.sh photoprism linux/amd64,linux/arm64 ce-buster /buster
|
||||
docker-release-impish:
|
||||
docker pull --platform=amd64 photoprism/develop:impish
|
||||
docker pull --platform=arm64 photoprism/develop:impish
|
||||
docker pull --platform=amd64 ubuntu:impish
|
||||
docker pull --platform=arm64 ubuntu:impish
|
||||
scripts/docker/buildx-multi.sh photoprism linux/amd64,linux/arm64 ce-impish /impish
|
||||
docker-release-jammy:
|
||||
docker pull --platform=amd64 photoprism/develop:jammy
|
||||
docker pull --platform=amd64 photoprism/develop:jammy-slim
|
||||
|
@ -469,12 +500,12 @@ docker-release-lunar:
|
|||
docker pull --platform=arm64 photoprism/develop:lunar
|
||||
docker pull --platform=arm64 photoprism/develop:lunar-slim
|
||||
scripts/docker/buildx-multi.sh photoprism linux/amd64,linux/arm64 ce /lunar
|
||||
docker-release-impish:
|
||||
docker pull --platform=amd64 photoprism/develop:impish
|
||||
docker pull --platform=arm64 photoprism/develop:impish
|
||||
docker pull --platform=amd64 ubuntu:impish
|
||||
docker pull --platform=arm64 ubuntu:impish
|
||||
scripts/docker/buildx-multi.sh photoprism linux/amd64,linux/arm64 ce-impish /impish
|
||||
docker-release-mantic:
|
||||
docker pull --platform=amd64 photoprism/develop:mantic
|
||||
docker pull --platform=amd64 photoprism/develop:mantic-slim
|
||||
docker pull --platform=arm64 photoprism/develop:mantic
|
||||
docker pull --platform=arm64 photoprism/develop:mantic-slim
|
||||
scripts/docker/buildx-multi.sh photoprism linux/amd64,linux/arm64 ce /mantic
|
||||
start-local:
|
||||
$(DOCKER_COMPOSE) -f docker-compose.local.yml up -d --wait
|
||||
stop-local:
|
||||
|
@ -501,8 +532,8 @@ terminal-latest:
|
|||
$(DOCKER_COMPOSE) -f docker-compose.latest.yml exec photoprism-latest bash
|
||||
logs-latest:
|
||||
$(DOCKER_COMPOSE) -f docker-compose.latest.yml logs -f photoprism-latest
|
||||
docker-local: docker-local-lunar
|
||||
docker-local-all: docker-local-lunar docker-local-jammy docker-local-bookworm docker-local-bullseye docker-local-buster
|
||||
docker-local: docker-local-mantic
|
||||
docker-local-all: docker-local-mantic docker-local-lunar docker-local-jammy docker-local-bookworm docker-local-bullseye docker-local-buster
|
||||
docker-local-bookworm:
|
||||
docker pull photoprism/develop:bookworm
|
||||
docker pull photoprism/develop:bookworm-slim
|
||||
|
@ -515,6 +546,10 @@ docker-local-buster:
|
|||
docker pull photoprism/develop:buster
|
||||
docker pull debian:buster-slim
|
||||
scripts/docker/build.sh photoprism ce-buster /buster "-t photoprism/photoprism:local"
|
||||
docker-local-impish:
|
||||
docker pull photoprism/develop:impish
|
||||
docker pull ubuntu:impish
|
||||
scripts/docker/build.sh photoprism ce-impish /impish "-t photoprism/photoprism:local"
|
||||
docker-local-jammy:
|
||||
docker pull photoprism/develop:jammy
|
||||
docker pull ubuntu:jammy
|
||||
|
@ -523,12 +558,12 @@ docker-local-lunar:
|
|||
docker pull photoprism/develop:lunar
|
||||
docker pull ubuntu:lunar
|
||||
scripts/docker/build.sh photoprism ce-lunar /lunar "-t photoprism/photoprism:local"
|
||||
docker-local-impish:
|
||||
docker pull photoprism/develop:impish
|
||||
docker pull ubuntu:impish
|
||||
scripts/docker/build.sh photoprism ce-impish /impish "-t photoprism/photoprism:local"
|
||||
docker-local-develop: docker-local-develop-lunar
|
||||
docker-local-develop-all: docker-local-develop-lunar docker-local-develop-jammy docker-local-develop-bookworm docker-local-develop-bullseye docker-local-develop-buster docker-local-develop-impish
|
||||
docker-local-mantic:
|
||||
docker pull photoprism/develop:mantic
|
||||
docker pull ubuntu:mantic
|
||||
scripts/docker/build.sh photoprism ce-mantic /mantic "-t photoprism/photoprism:local"
|
||||
docker-local-develop: docker-local-develop-mantic
|
||||
docker-local-develop-all: docker-local-develop-mantic docker-local-develop-lunar docker-local-develop-jammy docker-local-develop-bookworm docker-local-develop-bullseye docker-local-develop-buster docker-local-develop-impish
|
||||
docker-local-develop-bookworm:
|
||||
docker pull debian:bookworm-slim
|
||||
scripts/docker/build.sh develop bookworm /bookworm
|
||||
|
@ -538,15 +573,18 @@ docker-local-develop-bullseye:
|
|||
docker-local-develop-buster:
|
||||
docker pull golang:1-buster
|
||||
scripts/docker/build.sh develop buster /buster
|
||||
docker-local-develop-impish:
|
||||
docker pull ubuntu:impish
|
||||
scripts/docker/build.sh develop impish /impish
|
||||
docker-local-develop-jammy:
|
||||
docker pull ubuntu:jammy
|
||||
scripts/docker/build.sh develop jammy /jammy
|
||||
docker-local-develop-lunar:
|
||||
docker pull ubuntu:lunar
|
||||
scripts/docker/build.sh develop lunar /lunar
|
||||
docker-local-develop-impish:
|
||||
docker pull ubuntu:impish
|
||||
scripts/docker/build.sh develop impish /impish
|
||||
docker-local-develop-mantic:
|
||||
docker pull ubuntu:mantic
|
||||
scripts/docker/build.sh develop mantic /mantic
|
||||
docker-ddns:
|
||||
docker pull golang:alpine
|
||||
scripts/docker/buildx-multi.sh ddns linux/amd64,linux/arm64 $(BUILD_DATE)
|
||||
|
|
32
README.md
32
README.md
|
@ -2,20 +2,19 @@ PhotoPrism: Browse Your Life in Pictures
|
|||
========================================
|
||||
|
||||
[](https://docs.photoprism.app/license/agpl/)
|
||||
[](https://www.photoprism.app/about/team)
|
||||
[](https://docs.photoprism.app/)
|
||||
[](https://link.photoprism.app/chat)
|
||||
[](https://link.photoprism.app/discussions)
|
||||
[](https://link.photoprism.app/twitter)
|
||||
[](https://link.photoprism.app/reddit)
|
||||
[](https://photoprism.bsky.social/)
|
||||
[](https://floss.social/@photoprism)
|
||||
|
||||
PhotoPrism® is an AI-Powered Photos App for the [Decentralized Web](https://en.wikipedia.org/wiki/Decentralized_web).
|
||||
It makes use of the latest technologies to tag and find pictures automatically without getting in your way.
|
||||
You can run it at home, on a private server, or in the cloud.
|
||||
|
||||

|
||||

|
||||
|
||||
To get a first impression, you are welcome to play with our [public demo](https://try.photoprism.app/). Be careful not to upload any private pictures.
|
||||
To get a first impression, you are welcome to play with our [public demo](https://try.photoprism.app/). Please be careful not to upload any private, unlawful or offensive pictures.
|
||||
|
||||
## Feature Overview ##
|
||||
|
||||
|
@ -39,20 +38,12 @@ Being completely [**self-funded and independent**](https://link.photoprism.app/m
|
|||
## Getting Started ##
|
||||
<img align="right" width="25%" src="https://www.photoprism.app/user/pages/01.home/03._screenshots/iphone-maps-hybrid-540px.png">
|
||||
|
||||
Step-by-step installation instructions for our self-hosted [community edition](https://www.photoprism.app/get) can be found
|
||||
on [docs.photoprism.app](https://docs.photoprism.app/getting-started/) -
|
||||
all you need is a Web browser and [Docker](https://docs.docker.com/get-docker/) to run the server.
|
||||
It is available for Mac, Linux, and Windows.
|
||||
Step-by-step [installation instructions](https://docs.photoprism.app/getting-started/) for our self-hosted [community edition](https://link.photoprism.app/personal-editions) can be found on [docs.photoprism.app](https://docs.photoprism.app/getting-started/) - all you need is a Web browser and [Docker](https://docs.docker.com/get-docker/) to run the server. It is available for Mac, Linux, and Windows.
|
||||
|
||||
The [stable version](https://docs.photoprism.app/release-notes/) and development
|
||||
preview have been built into a single [multi-arch image](https://link.photoprism.app/docker-hub) for 64-bit AMD, Intel,
|
||||
and ARM processors. That means, [Raspberry Pi](https://docs.photoprism.app/getting-started/raspberry-pi/) 3 / 4 owners can pull
|
||||
from the same repository, enjoy the exact same functionality, and can follow the regular
|
||||
[installation instructions](https://docs.photoprism.app/getting-started/docker-compose/)
|
||||
after going through a short list of [requirements](https://docs.photoprism.app/getting-started/raspberry-pi/).
|
||||
The [stable releases](https://docs.photoprism.app/release-notes/) and [development preview](https://docs.photoprism.app/getting-started/updates/#development-preview) are available as a [multi-arch image](https://link.photoprism.app/docker-hub) for 64-bit AMD, Intel, and ARM processors.
|
||||
That means, [Raspberry Pi](https://docs.photoprism.app/getting-started/raspberry-pi/) and Apple Silicon users enjoy the exact same functionality and can follow the same [installation steps](https://docs.photoprism.app/getting-started/docker-compose/).
|
||||
|
||||
Existing users are advised to update their `docker-compose.yml` config based on our examples
|
||||
available at [dl.photoprism.app/docker](https://dl.photoprism.app/docker/).
|
||||
See our [Getting Started FAQ](https://docs.photoprism.app/getting-started/faq/#how-can-i-install-photoprism-without-docker) for alternative installation methods, for example using the [*tar.gz* packages](https://dl.photoprism.app/pkg/linux/README.html) we provide.
|
||||
|
||||
## Support Our Mission 💎 ##
|
||||
|
||||
|
@ -83,9 +74,8 @@ Please also leave [a star](https://github.com/photoprism/photoprism/stargazers)
|
|||
|
||||
## Getting Support ##
|
||||
|
||||
Visit [docs.photoprism.app/user-guide](https://docs.photoprism.app/user-guide/) to learn how to [sync](https://docs.photoprism.app/user-guide/sync/webdav/), [organize](https://docs.photoprism.app/user-guide/library/), and [share](https://docs.photoprism.app/user-guide/share/) your pictures. If you need help installing our software at home, you can [join us on Reddit](https://link.photoprism.app/reddit), ask in our [Community Chat](https://link.photoprism.app/chat), or post your question in [GitHub Discussions](https://link.photoprism.app/discussions).
|
||||
|
||||
Common problems can be quickly diagnosed and solved using the [Troubleshooting Checklists](https://docs.photoprism.app/getting-started/troubleshooting/) in [Getting Started](https://docs.photoprism.app/getting-started/). Eligible [members](https://link.photoprism.app/membership) are also welcome to [email us for technical support](https://www.photoprism.app/contact) and personalized advice.
|
||||
Visit [docs.photoprism.app/user-guide](https://docs.photoprism.app/user-guide/) to learn how to [sync](https://docs.photoprism.app/user-guide/sync/webdav/), [organize](https://docs.photoprism.app/user-guide/library/), and [share](https://docs.photoprism.app/user-guide/share/) your pictures. If you need help installing our software at home, you are welcome to post your question in [GitHub Discussions](https://link.photoprism.app/discussions) or ask in our [Community Chat](https://link.photoprism.app/chat).
|
||||
Common problems can be quickly diagnosed and solved using our [Troubleshooting Checklists](https://docs.photoprism.app/getting-started/troubleshooting/). Eligible [members](https://link.photoprism.app/membership) are also welcome to email us for technical support and advice.
|
||||
|
||||
## Upcoming Features and Enhancements ##
|
||||
|
||||
|
@ -113,7 +103,7 @@ Feel free to contact us at [hello@photoprism.app](mailto:hello@photoprism.app) w
|
|||
|
||||
## Every Contribution Makes a Difference ##
|
||||
|
||||
We welcome [contributions](CONTRIBUTING.md) of any kind, including blog posts, tutorials, testing, writing documentation, and pull requests. Our [Developer Guide](https://docs.photoprism.app/developer-guide/) contains all the information necessary for you to get started.
|
||||
We welcome [contributions](CONTRIBUTING.md) of any kind, including blog posts, tutorials, translations, testing, writing documentation, and pull requests. Our [Developer Guide](https://docs.photoprism.app/developer-guide/) contains all the information necessary for you to get started.
|
||||
|
||||
----
|
||||
|
||||
|
|
|
@ -20,6 +20,10 @@ You are [welcome to contact us](https://www.photoprism.app/contact) for change r
|
|||
|
||||
**Vitold Romanovski** (May 2023)
|
||||
|
||||
**Aaron C. de Bruyn** (September 2023)
|
||||
|
||||
[**Patrick Kvaksrud**](https://github.com/Kvaksrud) (October 2023)
|
||||
|
||||
## Gold Sponsors ##
|
||||
|
||||
[**Simen Eriksen**](https://github.com/dennorske) (GitHub Sponsors, December 2019)
|
||||
|
@ -68,6 +72,10 @@ You are [welcome to contact us](https://www.photoprism.app/contact) for change r
|
|||
|
||||
[**Yongho Lee**](https://github.com/lyh16) (Patreon, May 2023)
|
||||
|
||||
**Albert R** (Patreon, August 2023)
|
||||
|
||||
**Peter Galbavy** (Patreon, November 2023)
|
||||
|
||||
## Infrastructure Sponsors ##
|
||||
|
||||
Our project infrastructure is provided by the following companies:
|
||||
|
|
70
assets/examples/beach_sand.json
Normal file
70
assets/examples/beach_sand.json
Normal file
|
@ -0,0 +1,70 @@
|
|||
[{
|
||||
"SourceFile": "beach_sand.jpg",
|
||||
"ExifToolVersion": 12.56,
|
||||
"FileName": "beach_sand.jpg",
|
||||
"Directory": ".",
|
||||
"FileSize": 105321,
|
||||
"FileModifyDate": "2023:06:20 04:43:41+00:00",
|
||||
"FileAccessDate": "2023:08:06 16:38:50+00:00",
|
||||
"FileInodeChangeDate": "2023:08:04 05:10:37+00:00",
|
||||
"FilePermissions": 100644,
|
||||
"FileType": "JPEG",
|
||||
"FileTypeExtension": "JPG",
|
||||
"MIMEType": "image/jpeg",
|
||||
"JFIFVersion": "1 2",
|
||||
"ExifByteOrder": "MM",
|
||||
"Make": "Apple",
|
||||
"Model": "iPhone SE",
|
||||
"XResolution": 72,
|
||||
"YResolution": 72,
|
||||
"ResolutionUnit": 2,
|
||||
"Software": "10.2.1",
|
||||
"ModifyDate": "2017:02:15 14:13:40",
|
||||
"YCbCrPositioning": 1,
|
||||
"ExposureTime": 0.0004940711462,
|
||||
"ExposureProgram": 2,
|
||||
"ISO": 25,
|
||||
"ExifVersion": "0231",
|
||||
"DateTimeOriginal": "2017:02:15 14:13:40",
|
||||
"CreateDate": "2017:02:15 14:13:40",
|
||||
"ComponentsConfiguration": "1 2 3 0",
|
||||
"ApertureValue": 2.19999999733148,
|
||||
"MeteringMode": 5,
|
||||
"Flash": 16,
|
||||
"FocalLength": 4.2,
|
||||
"SubSecTimeOriginal": 249,
|
||||
"SubSecTimeDigitized": 249,
|
||||
"FlashpixVersion": "0100",
|
||||
"ColorSpace": 65535,
|
||||
"SensingMethod": 2,
|
||||
"ExposureMode": 0,
|
||||
"WhiteBalance": 0,
|
||||
"SceneCaptureType": 0,
|
||||
"LensModel": "iPhone SE back camera 4.15mm f/2.2",
|
||||
"GPSVersionID": "2 3 0 0",
|
||||
"GPSLatitudeRef": "S",
|
||||
"GPSLongitudeRef": "E",
|
||||
"GPSAltitudeRef": 1,
|
||||
"CurrentIPTCDigest": "804bedc723e0e6cd3a41d0a44b074d19",
|
||||
"DocumentNotes": "https://flickr.com/e/Rl7qi7oH%2BSEGDuwWBbZYaBQMEB5oNfPvQ6m3aMrPQ64%3D",
|
||||
"ApplicationRecordVersion": 4,
|
||||
"ImageWidth": 640,
|
||||
"ImageHeight": 480,
|
||||
"EncodingProcess": 2,
|
||||
"BitsPerSample": 8,
|
||||
"ColorComponents": 3,
|
||||
"YCbCrSubSampling": "2 2",
|
||||
"Aperture": 2.19999999733148,
|
||||
"ImageSize": "640 480",
|
||||
"Megapixels": 0.3072,
|
||||
"ShutterSpeed": 0.0004940711462,
|
||||
"SubSecCreateDate": "2017:02:15 14:13:40.249",
|
||||
"SubSecDateTimeOriginal": "2017:02:15 14:13:40.249",
|
||||
"GPSAltitude": -1.990417522,
|
||||
"GPSLatitude": -29.2824777777778,
|
||||
"GPSLongitude": 31.4436361111111,
|
||||
"FocalLength35efl": 4.2,
|
||||
"GPSPosition": "-29.2824777777778 31.4436361111111",
|
||||
"LightValue": 15.2580006188259,
|
||||
"LensID": "iPhone SE back camera 4.15mm f/2.2"
|
||||
}]
|
BIN
assets/examples/iphone_15_pro.heic
Normal file
BIN
assets/examples/iphone_15_pro.heic
Normal file
Binary file not shown.
153
assets/examples/iphone_15_pro.json
Normal file
153
assets/examples/iphone_15_pro.json
Normal file
|
@ -0,0 +1,153 @@
|
|||
[{
|
||||
"SourceFile": "iphone_15_pro.heic",
|
||||
"ExifToolVersion": 12.40,
|
||||
"FileName": "iphone_15_pro.heic",
|
||||
"Directory": ".",
|
||||
"FileSize": 886825,
|
||||
"FileModifyDate": "2023:10:31 11:48:48+01:00",
|
||||
"FileAccessDate": "2023:10:31 11:51:22+01:00",
|
||||
"FileInodeChangeDate": "2023:10:31 11:50:11+01:00",
|
||||
"FilePermissions": 100664,
|
||||
"FileType": "HEIC",
|
||||
"FileTypeExtension": "HEIC",
|
||||
"MIMEType": "image/heic",
|
||||
"MajorBrand": "heic",
|
||||
"MinorVersion": "0.0.0",
|
||||
"CompatibleBrands": ["mif1","MiHE","MiPr","miaf","MiHB","heic"],
|
||||
"HandlerType": "pict",
|
||||
"PrimaryItemReference": 49,
|
||||
"MetaImageSize": "0 1287 4032 3024",
|
||||
"XMPToolkit": "XMP Core 6.0.0",
|
||||
"HDRGainMapHeadroom": 3.847906,
|
||||
"HDRGainMapVersion": 131072,
|
||||
"ExifByteOrder": "MM",
|
||||
"Make": "Apple",
|
||||
"Model": "iPhone 15 Pro",
|
||||
"Orientation": 6,
|
||||
"XResolution": 72,
|
||||
"YResolution": 72,
|
||||
"ResolutionUnit": 2,
|
||||
"Software": 17.1,
|
||||
"ModifyDate": "2023:10:31 11:44:43",
|
||||
"HostComputer": "iPhone 15 Pro",
|
||||
"ExposureTime": 0.01666666667,
|
||||
"FNumber": 2.2,
|
||||
"ExposureProgram": 2,
|
||||
"ISO": 400,
|
||||
"ExifVersion": "0232",
|
||||
"DateTimeOriginal": "2023:10:31 11:44:43",
|
||||
"CreateDate": "2023:10:31 11:44:43",
|
||||
"OffsetTime": "+01:00",
|
||||
"OffsetTimeOriginal": "+01:00",
|
||||
"OffsetTimeDigitized": "+01:00",
|
||||
"ShutterSpeedValue": 0.0165679999782223,
|
||||
"ApertureValue": 2.20000000038133,
|
||||
"BrightnessValue": 2.15000162,
|
||||
"ExposureCompensation": 0,
|
||||
"MeteringMode": 5,
|
||||
"Flash": 16,
|
||||
"FocalLength": 2.22,
|
||||
"SubjectArea": "2015 1511 2323 1330",
|
||||
"RunTimeFlags": 1,
|
||||
"RunTimeValue": 154455128730208,
|
||||
"RunTimeScale": 1000000000,
|
||||
"RunTimeEpoch": 0,
|
||||
"AccelerationVector": "-0.004213878416 -1.002046108 -0.01470538881",
|
||||
"Warning": "[minor] Bad format (16) for MakerNotes entry 10",
|
||||
"SubSecTimeOriginal": 432,
|
||||
"SubSecTimeDigitized": 432,
|
||||
"ColorSpace": 65535,
|
||||
"ExifImageWidth": 4032,
|
||||
"ExifImageHeight": 3024,
|
||||
"SensingMethod": 2,
|
||||
"SceneType": 1,
|
||||
"ExposureMode": 0,
|
||||
"WhiteBalance": 0,
|
||||
"FocalLengthIn35mmFormat": 14,
|
||||
"LensInfo": "2.22 9 1.779999971 2.8",
|
||||
"LensMake": "Apple",
|
||||
"LensModel": "iPhone 15 Pro back triple camera 2.22mm f/2.2",
|
||||
"CompositeImage": 2,
|
||||
"GPSLatitudeRef": "N",
|
||||
"GPSLongitudeRef": "E",
|
||||
"GPSAltitudeRef": 0,
|
||||
"GPSTimeStamp": "10:44:43",
|
||||
"GPSSpeedRef": "K",
|
||||
"GPSSpeed": 0,
|
||||
"GPSImgDirectionRef": "T",
|
||||
"GPSImgDirection": 101.3112946,
|
||||
"GPSDestBearingRef": "T",
|
||||
"GPSDestBearing": 101.3112946,
|
||||
"GPSDateStamp": "2023:10:31",
|
||||
"GPSHPositioningError": 14.275587,
|
||||
"ProfileCMMType": "appl",
|
||||
"ProfileVersion": 1024,
|
||||
"ProfileClass": "mntr",
|
||||
"ColorSpaceData": "RGB ",
|
||||
"ProfileConnectionSpace": "XYZ ",
|
||||
"ProfileDateTime": "2022:01:01 00:00:00",
|
||||
"ProfileFileSignature": "acsp",
|
||||
"PrimaryPlatform": "APPL",
|
||||
"CMMFlags": 0,
|
||||
"DeviceManufacturer": "APPL",
|
||||
"DeviceModel": "",
|
||||
"DeviceAttributes": "0 0",
|
||||
"RenderingIntent": 0,
|
||||
"ConnectionSpaceIlluminant": "0.9642 1 0.82491",
|
||||
"ProfileCreator": "appl",
|
||||
"ProfileID": "236 253 163 142 56 133 71 195 109 180 189 79 122 218 24 47",
|
||||
"ProfileDescription": "Display P3",
|
||||
"ProfileCopyright": "Copyright Apple Inc., 2022",
|
||||
"MediaWhitePoint": "0.96419 1 0.82489",
|
||||
"RedMatrixColumn": "0.51512 0.2412 -0.00105",
|
||||
"GreenMatrixColumn": "0.29198 0.69225 0.04189",
|
||||
"BlueMatrixColumn": "0.1571 0.06657 0.78407",
|
||||
"RedTRC": "(Binary data 32 bytes, use -b option to extract)",
|
||||
"ChromaticAdaptation": "1.04788 0.02292 -0.0502 0.02959 0.99048 -0.01706 -0.00923 0.01508 0.75168",
|
||||
"BlueTRC": "(Binary data 32 bytes, use -b option to extract)",
|
||||
"GreenTRC": "(Binary data 32 bytes, use -b option to extract)",
|
||||
"HEVCConfigurationVersion": 1,
|
||||
"GeneralProfileSpace": 0,
|
||||
"GeneralTierFlag": 0,
|
||||
"GeneralProfileIDC": 3,
|
||||
"GenProfileCompatibilityFlags": 1879048192,
|
||||
"ConstraintIndicatorFlags": "176 0 0 0 0 0",
|
||||
"GeneralLevelIDC": 90,
|
||||
"MinSpatialSegmentationIDC": 0,
|
||||
"ParallelismType": 0,
|
||||
"ChromaFormat": 1,
|
||||
"BitDepthLuma": 8,
|
||||
"BitDepthChroma": 8,
|
||||
"AverageFrameRate": 0,
|
||||
"ConstantFrameRate": 0,
|
||||
"NumTemporalLayers": 1,
|
||||
"TemporalIDNested": 0,
|
||||
"ImageWidth": 4032,
|
||||
"ImageHeight": 3024,
|
||||
"ImageSpatialExtent": "4032 3024",
|
||||
"Rotation": 270,
|
||||
"ImagePixelDepth": 8,
|
||||
"AuxiliaryImageType": "urn:com:apple:photo:2020:aux:hdrgainmap",
|
||||
"MediaDataSize": 882139,
|
||||
"MediaDataOffset": 4686,
|
||||
"RunTimeSincePowerUp": 154455.128730208,
|
||||
"Aperture": 2.2,
|
||||
"ImageSize": "4032 3024",
|
||||
"Megapixels": 12.192768,
|
||||
"ScaleFactor35efl": 6.30630630630631,
|
||||
"ShutterSpeed": 0.01666666667,
|
||||
"SubSecCreateDate": "2023:10:31 11:44:43.432+01:00",
|
||||
"SubSecDateTimeOriginal": "2023:10:31 11:44:43.432+01:00",
|
||||
"SubSecModifyDate": "2023:10:31 11:44:43+01:00",
|
||||
"GPSAltitude": 50.15664187,
|
||||
"GPSDateTime": "2023:10:31 10:44:43Z",
|
||||
"GPSLatitude": 52.4596055555556,
|
||||
"GPSLongitude": 13.3218416666667,
|
||||
"CircleOfConfusion": "0.00476447847114884",
|
||||
"FOV": 104.250120754113,
|
||||
"FocalLength35efl": 14,
|
||||
"GPSPosition": "52.4596055555556 13.3218416666667",
|
||||
"HyperfocalDistance": 0.470184057236731,
|
||||
"LightValue": 6.18189764281985,
|
||||
"LensID": "iPhone 15 Pro back triple camera 2.22mm f/2.2"
|
||||
}]
|
BIN
assets/examples/samsung-motion-photo.jpg
Normal file
BIN
assets/examples/samsung-motion-photo.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.9 MiB |
82
assets/examples/samsung-motion-photo.json
Normal file
82
assets/examples/samsung-motion-photo.json
Normal file
|
@ -0,0 +1,82 @@
|
|||
[{
|
||||
"SourceFile": "/go/src/github.com/photoprism/photoprism/storage/import/samsung-motion-photo.jpg",
|
||||
"ExifToolVersion": 12.56,
|
||||
"FileName": "samsung-motion-photo.jpg",
|
||||
"Directory": "/go/src/github.com/photoprism/photoprism/storage/import",
|
||||
"FileSize": 7221645,
|
||||
"FileModifyDate": "2023:07:27 02:03:13+00:00",
|
||||
"FileAccessDate": "2023:08:13 17:11:04+00:00",
|
||||
"FileInodeChangeDate": "2023:08:13 17:11:02+00:00",
|
||||
"FilePermissions": 100644,
|
||||
"FileType": "JPEG",
|
||||
"FileTypeExtension": "JPG",
|
||||
"MIMEType": "image/jpeg",
|
||||
"ExifByteOrder": "II",
|
||||
"Make": "samsung",
|
||||
"Model": "SM-G973F",
|
||||
"Orientation": 1,
|
||||
"XResolution": 72,
|
||||
"YResolution": 72,
|
||||
"ResolutionUnit": 2,
|
||||
"Software": "G973FXXU4CTC9",
|
||||
"ModifyDate": "2020:04:23 18:33:41",
|
||||
"YCbCrPositioning": 1,
|
||||
"ExposureTime": 0.0007575757576,
|
||||
"FNumber": 2.4,
|
||||
"ExposureProgram": 2,
|
||||
"ISO": 50,
|
||||
"ExifVersion": "0220",
|
||||
"DateTimeOriginal": "2020:04:23 18:33:41",
|
||||
"CreateDate": "2020:04:23 18:33:41",
|
||||
"ShutterSpeedValue": 0.999475026346474,
|
||||
"ApertureValue": 2.39495740923786,
|
||||
"BrightnessValue": 22.58,
|
||||
"ExposureCompensation": 0,
|
||||
"MaxApertureValue": 2.39495740923786,
|
||||
"MeteringMode": 2,
|
||||
"Flash": 0,
|
||||
"FocalLength": 4.32,
|
||||
"ColorSpace": 1,
|
||||
"ExifImageWidth": 4032,
|
||||
"ExifImageHeight": 3024,
|
||||
"ExposureMode": 0,
|
||||
"WhiteBalance": 0,
|
||||
"DigitalZoomRatio": 1,
|
||||
"FocalLengthIn35mmFormat": 26,
|
||||
"SceneCaptureType": 0,
|
||||
"ImageUniqueID": "L12XLLD01VM",
|
||||
"GPSLatitudeRef": "N",
|
||||
"GPSLongitudeRef": "W",
|
||||
"Compression": 6,
|
||||
"ThumbnailOffset": 888,
|
||||
"ThumbnailLength": 50555,
|
||||
"XMPToolkit": "Adobe XMP Core 5.1.0-jc003",
|
||||
"MicroVideo": 1,
|
||||
"MicroVideoVersion": 1,
|
||||
"MicroVideoOffset": 4535831,
|
||||
"MicroVideoPresentationTimestampUs": -1,
|
||||
"ImageWidth": 4032,
|
||||
"ImageHeight": 3024,
|
||||
"EncodingProcess": 0,
|
||||
"BitsPerSample": 8,
|
||||
"ColorComponents": 3,
|
||||
"YCbCrSubSampling": "2 2",
|
||||
"TimeStamp": "2020:04:23 17:33:41.809+00:00",
|
||||
"MCCData": 234,
|
||||
"EmbeddedVideoType": "MotionPhoto_Data",
|
||||
"EmbeddedVideoFile": "(Binary data 4535775 bytes, use -b option to extract)",
|
||||
"Aperture": 2.4,
|
||||
"ImageSize": "4032 3024",
|
||||
"Megapixels": 12.192768,
|
||||
"ScaleFactor35efl": 6.01851851851852,
|
||||
"ShutterSpeed": 0.0007575757576,
|
||||
"ThumbnailImage": "(Binary data 50555 bytes, use -b option to extract)",
|
||||
"GPSLatitude": 51.5049828,
|
||||
"GPSLongitude": -0.0787347997222222,
|
||||
"CircleOfConfusion": "0.00499230176602706",
|
||||
"FOV": 69.3903656740024,
|
||||
"FocalLength35efl": 26,
|
||||
"GPSPosition": "51.5049828 -0.0787347997222222",
|
||||
"HyperfocalDistance": 1.55759815100044,
|
||||
"LightValue": 13.8923910258672
|
||||
}]
|
|
@ -2,16 +2,16 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-02-09 12:51+0000\n"
|
||||
"PO-Revision-Date: 2023-02-09 13:13+0000\n"
|
||||
"Last-Translator: Anonymous <noreply@weblate.org>\n"
|
||||
"POT-Creation-Date: 2023-03-09 13:14+0000\n"
|
||||
"PO-Revision-Date: 2023-10-16 16:35+0000\n"
|
||||
"Last-Translator: dtsolakis <dtsola@eranet.gr>\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: el\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.15.2\n"
|
||||
"X-Generator: Weblate 5.0.2\n"
|
||||
|
||||
#: messages.go:94
|
||||
msgid "Something went wrong, try again"
|
||||
|
@ -19,7 +19,7 @@ msgstr "Κάτι πήγε στραβά, δοκιμάστε ξανά"
|
|||
|
||||
#: messages.go:95
|
||||
msgid "Unable to do that"
|
||||
msgstr "Δεν είναι δυνατόν να το κάνετε αυτό"
|
||||
msgstr "Αυτό δεν είναι εφικτό"
|
||||
|
||||
#: messages.go:96
|
||||
msgid "Changes could not be saved"
|
||||
|
@ -334,7 +334,7 @@ msgstr "Επεξεργασία μεταφόρτωσης..."
|
|||
|
||||
#: messages.go:172
|
||||
msgid "Upload has been processed"
|
||||
msgstr "%s έχει αποκατασταθεί"
|
||||
msgstr "Η φόρτωση έχει ολοκληρωθεί"
|
||||
|
||||
#: messages.go:173
|
||||
msgid "Selection approved"
|
||||
|
|
|
@ -2,9 +2,9 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-02-09 12:51+0000\n"
|
||||
"PO-Revision-Date: 2023-02-09 13:13+0000\n"
|
||||
"Last-Translator: Anonymous <noreply@weblate.org>\n"
|
||||
"POT-Creation-Date: 2023-03-09 13:14+0000\n"
|
||||
"PO-Revision-Date: 2023-06-07 08:37+0000\n"
|
||||
"Last-Translator: leosamuele221 <leosamuele221@gmail.com>\n"
|
||||
"Language-Team: Italian <https://translate.photoprism.app/projects/photoprism/"
|
||||
"backend/it/>\n"
|
||||
"Language: it\n"
|
||||
|
@ -12,11 +12,11 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.15.2\n"
|
||||
"X-Generator: Weblate 4.17\n"
|
||||
|
||||
#: messages.go:94
|
||||
msgid "Something went wrong, try again"
|
||||
msgstr "Qualcosa è andato storto, riprovare"
|
||||
msgstr "Qualcosa è andato storto, riprova"
|
||||
|
||||
#: messages.go:95
|
||||
msgid "Unable to do that"
|
||||
|
|
|
@ -2,9 +2,9 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-02-09 12:51+0000\n"
|
||||
"PO-Revision-Date: 2023-02-09 13:13+0000\n"
|
||||
"Last-Translator: Anonymous <noreply@weblate.org>\n"
|
||||
"POT-Creation-Date: 2023-03-09 13:14+0000\n"
|
||||
"PO-Revision-Date: 2023-06-07 08:37+0000\n"
|
||||
"Last-Translator: Admin <hello@photoprism.app>\n"
|
||||
"Language-Team: Slovak <https://translate.photoprism.app/projects/photoprism/"
|
||||
"backend/sk/>\n"
|
||||
"Language: sk\n"
|
||||
|
@ -12,7 +12,7 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
|
||||
"X-Generator: Weblate 4.15.2\n"
|
||||
"X-Generator: Weblate 4.17\n"
|
||||
|
||||
#: messages.go:94
|
||||
msgid "Something went wrong, try again"
|
||||
|
@ -244,7 +244,7 @@ msgstr "Účet uložený"
|
|||
|
||||
#: messages.go:151
|
||||
msgid "Account deleted"
|
||||
msgstr "Účet ostránený"
|
||||
msgstr "Účet odstránený"
|
||||
|
||||
#: messages.go:152
|
||||
msgid "Settings saved"
|
||||
|
|
BIN
assets/static/img/404.jpg
Normal file
BIN
assets/static/img/404.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 105 KiB |
|
@ -26,8 +26,8 @@
|
|||
<div class="splash-logo">
|
||||
{{template "logo.gohtml" .}}
|
||||
</div>
|
||||
<progress id="progress" class="html-progress" max="100"></progress>
|
||||
<progress id="progress" class="html-progress" max="100" style="accent-color: #c8c2e8; color-scheme: dark;"></progress>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="busy-overlay"><div class="splash-center"><progress id="busy-progress" class="html-progress" max="100"></progress></div></div>
|
||||
<div id="busy-overlay"><div class="splash-center"><progress id="busy-progress" class="html-progress" max="100" style="accent-color: #c8c2e8; color-scheme: dark;"></progress></div></div>
|
128
docker-compose.armv7.yml
Normal file
128
docker-compose.armv7.yml
Normal file
|
@ -0,0 +1,128 @@
|
|||
version: '3.5'
|
||||
|
||||
## FOR ARMv7 TEST AND DEVELOPMENT ONLY, DO NOT USE IN PRODUCTION ##
|
||||
## Setup: https://docs.photoprism.app/developer-guide/setup/ ##
|
||||
|
||||
services:
|
||||
## App Server
|
||||
photoprism:
|
||||
build: .
|
||||
image: photoprism/photoprism:develop
|
||||
platform: "arm"
|
||||
depends_on:
|
||||
- mariadb
|
||||
- dummy-webdav
|
||||
security_opt:
|
||||
- seccomp:unconfined
|
||||
- apparmor:unconfined
|
||||
ports:
|
||||
- "2342:2342" # Default HTTP port (host:container)
|
||||
- "2343:2343" # Acceptance Test HTTP port (host:container)
|
||||
shm_size: "2gb"
|
||||
environment:
|
||||
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # The initial admin password (min 4 characters)
|
||||
PHOTOPRISM_UID: ${UID:-1000}
|
||||
PHOTOPRISM_GID: ${GID:-1000}
|
||||
PHOTOPRISM_SITE_URL: "http://localhost:2342/"
|
||||
PHOTOPRISM_SITE_TITLE: "PhotoPrism"
|
||||
PHOTOPRISM_SITE_CAPTION: "Browse Your Life"
|
||||
PHOTOPRISM_SITE_DESCRIPTION: "AI-powered app for browsing, organizing & sharing your photo collection."
|
||||
PHOTOPRISM_SITE_AUTHOR: "@photoprism_app"
|
||||
PHOTOPRISM_DEBUG: "true"
|
||||
PHOTOPRISM_READONLY: "false"
|
||||
PHOTOPRISM_PUBLIC: "true"
|
||||
PHOTOPRISM_EXPERIMENTAL: "true"
|
||||
PHOTOPRISM_SERVER_MODE: "debug"
|
||||
PHOTOPRISM_HTTP_HOST: "0.0.0.0"
|
||||
PHOTOPRISM_HTTP_PORT: 2342
|
||||
PHOTOPRISM_HTTP_COMPRESSION: "gzip" # Improves transfer speed and bandwidth utilization (none or gzip)
|
||||
PHOTOPRISM_DATABASE_DRIVER: "mysql"
|
||||
PHOTOPRISM_DATABASE_SERVER: "mariadb:4001"
|
||||
PHOTOPRISM_DATABASE_NAME: "photoprism"
|
||||
PHOTOPRISM_DATABASE_USER: "root"
|
||||
PHOTOPRISM_DATABASE_PASSWORD: "photoprism"
|
||||
PHOTOPRISM_TEST_DRIVER: "sqlite"
|
||||
PHOTOPRISM_TEST_DSN: ".test.db"
|
||||
PHOTOPRISM_TEST_DSN_MYSQL8: "root:photoprism@tcp(mysql:4001)/photoprism?charset=utf8mb4,utf8&collation=utf8mb4_unicode_ci&parseTime=true"
|
||||
PHOTOPRISM_ASSETS_PATH: "/go/src/github.com/photoprism/photoprism/assets"
|
||||
PHOTOPRISM_STORAGE_PATH: "/go/src/github.com/photoprism/photoprism/storage"
|
||||
PHOTOPRISM_ORIGINALS_PATH: "/go/src/github.com/photoprism/photoprism/storage/originals"
|
||||
PHOTOPRISM_IMPORT_PATH: "/go/src/github.com/photoprism/photoprism/storage/import"
|
||||
PHOTOPRISM_DISABLE_CHOWN: "false" # Disables storage permission updates on startup
|
||||
PHOTOPRISM_DISABLE_BACKUPS: "false" # Don't backup photo and album metadata to YAML files
|
||||
PHOTOPRISM_DISABLE_WEBDAV: "false" # Disables built-in WebDAV server
|
||||
PHOTOPRISM_DISABLE_SETTINGS: "false" # Disables Settings in Web UI
|
||||
PHOTOPRISM_DISABLE_PLACES: "false" # Disables reverse geocoding and maps
|
||||
PHOTOPRISM_DISABLE_EXIFTOOL: "false" # Don't create ExifTool JSON files for improved metadata extraction
|
||||
PHOTOPRISM_DISABLE_TENSORFLOW: "false" # Don't use TensorFlow for image classification
|
||||
PHOTOPRISM_DETECT_NSFW: "false" # Flag photos as private that MAY be offensive (requires TensorFlow)
|
||||
PHOTOPRISM_UPLOAD_NSFW: "false" # Allows uploads that may be offensive
|
||||
PHOTOPRISM_DARKTABLE_PRESETS: "false" # Enables Darktable presets and disables concurrent RAW conversion
|
||||
PHOTOPRISM_THUMB_FILTER: "lanczos" # Resample filter, best to worst: blackman, lanczos, cubic, linear
|
||||
PHOTOPRISM_THUMB_UNCACHED: "true" # Enables on-demand thumbnail rendering (high memory and cpu usage)
|
||||
PHOTOPRISM_THUMB_SIZE: 2048 # Pre-rendered thumbnail size limit (default 2048, min 720, max 7680)
|
||||
# PHOTOPRISM_THUMB_SIZE: 4096 # Retina 4K, DCI 4K (requires more storage); 7680 for 8K Ultra HD
|
||||
PHOTOPRISM_THUMB_SIZE_UNCACHED: 7680 # On-demand rendering size limit (default 7680, min 720, max 7680)
|
||||
PHOTOPRISM_JPEG_SIZE: 7680 # Size limit for converted image files in pixels (720-30000)
|
||||
PHOTOPRISM_JPEG_QUALITY: 92 # Set to 95 for high-quality thumbnails (25-100)
|
||||
TF_CPP_MIN_LOG_LEVEL: 0 # Show TensorFlow log messages for development
|
||||
## Enable TensorFlow AVX2 support for modern Intel CPUs (requires starting the container as root):
|
||||
# PHOTOPRISM_INIT: "tensorflow-amd64-avx2"
|
||||
## Hardware video transcoding config (optional):
|
||||
# PHOTOPRISM_FFMPEG_BUFFERS: "64" # FFmpeg capture buffers (default: 32)
|
||||
# PHOTOPRISM_FFMPEG_BITRATE: "32" # FFmpeg encoding bitrate limit in Mbit/s (default: 50)
|
||||
# PHOTOPRISM_FFMPEG_ENCODER: "h264_v4l2m2m" # Use Video4Linux for AVC transcoding (default: libx264)
|
||||
# PHOTOPRISM_FFMPEG_ENCODER: "h264_qsv" # Use Intel Quick Sync Video for AVC transcoding (default: libx264)
|
||||
# PHOTOPRISM_INIT: "intel-graphics tensorflow-amd64-avx2" # Enable TensorFlow AVX2 & Intel Graphics support
|
||||
# PHOTOPRISM_INIT: "install-updates" # Installs general operating system updates
|
||||
## Hardware devices for video transcoding and machine learning (optional):
|
||||
# devices:
|
||||
# - "/dev/video11:/dev/video11" # Video4Linux (h264_v4l2m2m)
|
||||
# - "/dev/dri/renderD128:/dev/dri/renderD128" # Intel GPU
|
||||
# - "/dev/dri/card0:/dev/dri/card0"
|
||||
working_dir: "/go/src/github.com/photoprism/photoprism"
|
||||
volumes:
|
||||
- ".:/go/src/github.com/photoprism/photoprism"
|
||||
- "go-mod:/go/pkg/mod"
|
||||
|
||||
## MariaDB Database Server
|
||||
## Docs: https://mariadb.com/docs/reference/
|
||||
## Release Notes: https://mariadb.com/kb/en/changes-improvements-in-mariadb-1011/
|
||||
mariadb:
|
||||
image: mariadb:10.11
|
||||
security_opt: # see https://github.com/MariaDB/mariadb-docker/issues/434#issuecomment-1136151239
|
||||
- seccomp:unconfined
|
||||
- apparmor:unconfined
|
||||
command: --port=4001 --innodb-strict-mode=1 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
expose:
|
||||
- "4001"
|
||||
ports:
|
||||
- "4001:4001" # database port (host:container)
|
||||
volumes:
|
||||
- "./scripts/sql/mariadb-init.sql:/docker-entrypoint-initdb.d/init.sql"
|
||||
environment:
|
||||
MARIADB_AUTO_UPGRADE: "1"
|
||||
MARIADB_INITDB_SKIP_TZINFO: "1"
|
||||
MARIADB_DATABASE: "photoprism"
|
||||
MARIADB_USER: "photoprism"
|
||||
MARIADB_PASSWORD: "photoprism"
|
||||
MARIADB_ROOT_PASSWORD: "photoprism"
|
||||
|
||||
## Dummy WebDAV Server
|
||||
dummy-webdav:
|
||||
image: photoprism/dummy-webdav:231015
|
||||
container_name: dummy-webdav
|
||||
environment:
|
||||
WEBDAV_USERNAME: admin
|
||||
WEBDAV_PASSWORD: photoprism
|
||||
|
||||
## Create named volume for Go module cache
|
||||
volumes:
|
||||
go-mod:
|
||||
driver: local
|
||||
|
||||
## Create shared "photoprism-develop" network for connecting with services in other docker-compose.yml files
|
||||
networks:
|
||||
default:
|
||||
name: shared
|
||||
driver: bridge
|
|
@ -22,7 +22,7 @@ services:
|
|||
PHOTOPRISM_ADMIN_USER: "admin" # admin login username
|
||||
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # initial admin password (8-72 characters)
|
||||
PHOTOPRISM_AUTH_MODE: "public" # authentication mode (public, password)
|
||||
PHOTOPRISM_SITE_URL: "http://photoprism.me:2342/"
|
||||
PHOTOPRISM_SITE_URL: "http://localhost:2342/"
|
||||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||
PHOTOPRISM_SITE_DESCRIPTION: "Open-Source Photo Management"
|
||||
PHOTOPRISM_SITE_AUTHOR: "@photoprism_app"
|
||||
|
@ -150,7 +150,7 @@ services:
|
|||
security_opt: # see https://github.com/MariaDB/mariadb-docker/issues/434#issuecomment-1136151239
|
||||
- seccomp:unconfined
|
||||
- apparmor:unconfined
|
||||
command: mariadbd --port=4001 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
command: --port=4001 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
expose:
|
||||
- "4001" # database port (internal)
|
||||
volumes:
|
||||
|
@ -165,11 +165,11 @@ services:
|
|||
|
||||
## Dummy OpenID Connect Provider
|
||||
dummy-oidc:
|
||||
image: photoprism/dummy-oidc:220405
|
||||
image: photoprism/dummy-oidc:231015
|
||||
|
||||
## Dummy WebDAV Server
|
||||
dummy-webdav:
|
||||
image: photoprism/dummy-webdav:20211109
|
||||
image: photoprism/dummy-webdav:231015
|
||||
environment:
|
||||
WEBDAV_USERNAME: admin
|
||||
WEBDAV_PASSWORD: photoprism
|
||||
|
|
|
@ -4,15 +4,15 @@ version: '3.5'
|
|||
## Setup: https://docs.photoprism.app/developer-guide/setup/ ##
|
||||
|
||||
services:
|
||||
## MariaDB 11.0 Database Server
|
||||
## MariaDB 11.2 Database Server
|
||||
## Docs: https://mariadb.com/docs/reference/
|
||||
## Release Notes: https://mariadb.com/kb/en/mariadb-11-0-0-release-notes/
|
||||
mariadb-11-0:
|
||||
image: mariadb:11.0-rc
|
||||
## Release Notes: https://mariadb.com/kb/en/release-notes-mariadb-11-2-series/
|
||||
mariadb-11-2:
|
||||
image: mariadb:11.2
|
||||
security_opt: # see https://github.com/MariaDB/mariadb-docker/issues/434#issuecomment-1136151239
|
||||
- seccomp:unconfined
|
||||
- apparmor:unconfined
|
||||
command: mariadbd --port=4001 --innodb-strict-mode=1 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
command: --port=4001 --innodb-strict-mode=1 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
expose:
|
||||
- "4001"
|
||||
ports:
|
||||
|
@ -27,14 +27,14 @@ services:
|
|||
MARIADB_PASSWORD: "photoprism"
|
||||
MARIADB_ROOT_PASSWORD: "photoprism"
|
||||
|
||||
## MariaDB 10.8 Database Server
|
||||
## Docs: https://mariadb.com/kb/en/release-notes-mariadb-108-series/
|
||||
mariadb-10-8:
|
||||
image: mariadb:10.8
|
||||
## MariaDB 10.11 Database Server
|
||||
## Docs: https://mariadb.com/kb/en/release-notes-mariadb-1011-series/
|
||||
mariadb-10-11:
|
||||
image: mariadb:10.11
|
||||
security_opt: # see https://github.com/MariaDB/mariadb-docker/issues/434#issuecomment-1136151239
|
||||
- seccomp:unconfined
|
||||
- apparmor:unconfined
|
||||
command: mariadbd --port=4001 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
command: --port=4001 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
expose:
|
||||
- "4001"
|
||||
ports:
|
||||
|
@ -49,28 +49,12 @@ services:
|
|||
MARIADB_PASSWORD: "photoprism"
|
||||
MARIADB_ROOT_PASSWORD: "photoprism"
|
||||
|
||||
## MariaDB 10.7 Database Server
|
||||
mariadb-10-7:
|
||||
image: mariadb:10.7
|
||||
command: mysqld --port=4001 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
expose:
|
||||
- "4001" # database port (internal)
|
||||
volumes:
|
||||
- "./scripts/sql/mariadb-init.sql:/docker-entrypoint-initdb.d/init.sql"
|
||||
environment:
|
||||
MARIADB_AUTO_UPGRADE: "1"
|
||||
MARIADB_INITDB_SKIP_TZINFO: "1"
|
||||
MARIADB_DATABASE: "photoprism"
|
||||
MARIADB_USER: "photoprism"
|
||||
MARIADB_PASSWORD: "photoprism"
|
||||
MARIADB_ROOT_PASSWORD: "photoprism"
|
||||
|
||||
## MariaDB 10.5.5 Database Server
|
||||
## Affected by MDEV-25362: Incorrect name resolution for subqueries in ON expressions
|
||||
## see https://jira.mariadb.org/browse/MDEV-25362
|
||||
mariadb-10-5-5:
|
||||
image: mariadb:10.5.5
|
||||
command: mysqld --port=4001 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
command: --port=4001 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
expose:
|
||||
- "4001" # database port (internal)
|
||||
volumes:
|
||||
|
@ -85,7 +69,7 @@ services:
|
|||
## Docs: https://mariadb.com/docs/reference/cs10.3/
|
||||
mariadb-10-3:
|
||||
image: mariadb:10.3
|
||||
command: mysqld --port=4001 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
command: --port=4001 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
expose:
|
||||
- "4001" # database port (internal)
|
||||
volumes:
|
||||
|
@ -100,7 +84,7 @@ services:
|
|||
## Docs: https://mariadb.com/docs/reference/cs10.2/
|
||||
mariadb-10-2:
|
||||
image: mariadb:10.2
|
||||
command: mysqld --port=4001 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
command: --port=4001 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
expose:
|
||||
- "4001" # database port (internal)
|
||||
volumes:
|
||||
|
@ -114,7 +98,7 @@ services:
|
|||
## MariaDB 10.1 Database Server
|
||||
mariadb-10-1:
|
||||
image: mariadb:10.1
|
||||
command: mysqld --port=4001 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
command: --port=4001 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
expose:
|
||||
- "4001" # database port (internal)
|
||||
volumes:
|
||||
|
|
|
@ -8,7 +8,7 @@ services:
|
|||
## Docs: https://dev.mysql.com/doc/refman/8.0/en/
|
||||
mysql:
|
||||
image: mysql:8
|
||||
command: mysqld --port=4001 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
command: --port=4001 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
expose:
|
||||
- "4001" # database port (internal)
|
||||
environment:
|
||||
|
|
|
@ -30,7 +30,7 @@ services:
|
|||
PHOTOPRISM_ADMIN_USER: "admin" # admin login username
|
||||
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # initial admin password (8-72 characters)
|
||||
PHOTOPRISM_AUTH_MODE: "password" # authentication mode (public, password)
|
||||
PHOTOPRISM_SITE_URL: "http://photoprism.me:2342/"
|
||||
PHOTOPRISM_SITE_URL: "http://localhost:2342/"
|
||||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||
PHOTOPRISM_SITE_DESCRIPTION: "Open-Source Photo Management"
|
||||
PHOTOPRISM_SITE_AUTHOR: "@photoprism_app"
|
||||
|
@ -83,7 +83,7 @@ services:
|
|||
|
||||
## Dummy WebDAV Server
|
||||
dummy-webdav:
|
||||
image: photoprism/dummy-webdav:20211109
|
||||
image: photoprism/dummy-webdav:231015
|
||||
environment:
|
||||
WEBDAV_USERNAME: admin
|
||||
WEBDAV_PASSWORD: photoprism
|
||||
|
|
|
@ -62,7 +62,7 @@ services:
|
|||
PHOTOPRISM_OIDC_CLIENT: "photoprism-develop"
|
||||
PHOTOPRISM_OIDC_SECRET: "9d8351a0-ca01-4556-9c37-85eb634869b9"
|
||||
## Site Information
|
||||
PHOTOPRISM_SITE_URL: "http://photoprism.me:2342/" # server URL in the format "http(s)://domain.name(:port)/(path)"
|
||||
PHOTOPRISM_SITE_URL: "http://localhost:2342/" # server URL in the format "http(s)://domain.name(:port)/(path)"
|
||||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||
PHOTOPRISM_SITE_DESCRIPTION: "Tags and finds pictures without getting in your way!"
|
||||
PHOTOPRISM_SITE_AUTHOR: "@photoprism_app"
|
||||
|
@ -103,13 +103,13 @@ services:
|
|||
PHOTOPRISM_JPEG_SIZE: 7680 # size limit for converted image files in pixels (720-30000)
|
||||
PHOTOPRISM_JPEG_QUALITY: 85 # a higher value increases the quality and file size of JPEG images and thumbnails (25-100)
|
||||
TF_CPP_MIN_LOG_LEVEL: 0 # show TensorFlow log messages for development
|
||||
## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/):
|
||||
# PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi)
|
||||
# PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840)
|
||||
# PHOTOPRISM_FFMPEG_BITRATE: "32" # video bitrate limit in Mbit/s (default: 50)
|
||||
# LIBVA_DRIVER_NAME: "i965" # For Intel architectures Haswell and older which do not support QSV yet but use VAAPI instead
|
||||
## Run/install on first startup (options: update https gpu tensorflow davfs clitools clean):
|
||||
PHOTOPRISM_INIT: "https tensorflow"
|
||||
## Hardware Video Transcoding (optional):
|
||||
# PHOTOPRISM_FFMPEG_ENCODER: "nvidia" # FFmpeg encoder ("software", "intel", "nvidia", "apple", "raspberry", "vaapi") Intel: "intel" for Broadwell or later and "vaapi" for Haswell or earlier
|
||||
# PHOTOPRISM_FFMPEG_ENCODER: "intel" # FFmpeg encoder ("software", "intel", "nvidia", "apple", "raspberry", "vaapi") Intel: "intel" for Broadwell or later and "vaapi" for Haswell or earlier`
|
||||
# PHOTOPRISM_FFMPEG_BITRATE: "32" # FFmpeg encoding bitrate limit in Mbit/s (default: 50)
|
||||
# LIBVA_DRIVER_NAME: "i965" # For Intel architectures Haswell and older which do not support QSV yet but use VAAPI instead
|
||||
## Share hardware devices with FFmpeg and TensorFlow (optional):
|
||||
# devices:
|
||||
# - "/dev/dri:/dev/dri" # Intel QSV (Broadwell and later) or VAAPI (Haswell and earlier)
|
||||
|
@ -133,7 +133,7 @@ services:
|
|||
security_opt: # see https://github.com/MariaDB/mariadb-docker/issues/434#issuecomment-1136151239
|
||||
- seccomp:unconfined
|
||||
- apparmor:unconfined
|
||||
command: mariadbd --port=4001 --innodb-strict-mode=1 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
command: --port=4001 --innodb-strict-mode=1 --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
expose:
|
||||
- "4001"
|
||||
ports:
|
||||
|
@ -165,7 +165,7 @@ services:
|
|||
## Login: user / photoprism
|
||||
## Admin: admin / photoprism
|
||||
keycloak:
|
||||
image: quay.io/keycloak/keycloak:20.0
|
||||
image: quay.io/keycloak/keycloak:22.0
|
||||
command: "start-dev" # development mode, do not use this in production!
|
||||
container_name: keycloak
|
||||
links:
|
||||
|
@ -211,7 +211,7 @@ services:
|
|||
|
||||
## Dummy OpenID Connect Provider
|
||||
dummy-oidc:
|
||||
image: photoprism/dummy-oidc:220405
|
||||
image: photoprism/dummy-oidc:231015
|
||||
container_name: dummy-oidc
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
|
@ -224,7 +224,7 @@ services:
|
|||
|
||||
## Dummy WebDAV Server
|
||||
dummy-webdav:
|
||||
image: photoprism/dummy-webdav:220405
|
||||
image: photoprism/dummy-webdav:231015
|
||||
container_name: dummy-webdav
|
||||
environment:
|
||||
WEBDAV_USERNAME: admin
|
||||
|
|
|
@ -1,64 +1,21 @@
|
|||
# Dockerfiles and Docker Compose Examples
|
||||
# Dockerfiles for Development and Production
|
||||
|
||||
[**Dockerfiles**](https://docs.docker.com/engine/reference/builder/) are text documents that contain all commands a user
|
||||
could call in a terminal to assemble an application image.
|
||||
[**Dockerfiles**](https://docs.docker.com/engine/reference/builder/) are text documents that contain all commands a user could call in a terminal to assemble an application image.
|
||||
|
||||
[**Docker Compose**](https://docs.docker.com/compose/) uses [human-friendly YAML files](https://docs.photoprism.app/developer-guide/technologies/yaml/)
|
||||
to configure all application services so you can easily start them with a single command.
|
||||
[**Docker Compose**](https://docs.docker.com/compose/) uses [human-friendly YAML files](https://docs.photoprism.app/developer-guide/technologies/yaml/) to configure all application services so you can easily start them with a single command.
|
||||
|
||||
## Why are we using Docker? ##
|
||||
See our [Getting Started FAQ](https://docs.photoprism.app/getting-started/faq/#how-can-i-install-photoprism-without-docker) for alternative installation methods, for example using the [*tar.gz* packages](https://github.com/photoprism/photoprism/blob/develop/setup/pkg/linux/README.md) we provide for download at [dl.photoprism.app/pkg/linux/](https://dl.photoprism.app/pkg/linux/README.html).
|
||||
|
||||
Containers are nothing new; [Solaris Zones](https://en.wikipedia.org/wiki/Solaris_Containers) have been around for
|
||||
about 15 years, first released publicly in 2004. The chroot system call was introduced during
|
||||
[development of Version 7 Unix in 1979](https://en.wikipedia.org/wiki/Chroot). It is used ever since for hosting
|
||||
applications exposed to the public Internet.
|
||||
## What are the benefits of using Docker? ##
|
||||
|
||||
Modern Linux containers are an incremental enhancement. A main advantage of Docker is that application images
|
||||
can be easily made available to users via Internet. It provides a common standard across most operating
|
||||
systems and devices, which saves our team a lot of time that we can then spend [more effectively](https://docs.photoprism.app/developer-guide/issues/#effectiveness-efficiency), for example,
|
||||
providing support and developing one of the many features that users are waiting for.
|
||||
**(1) Docker uses standard features of the Linux kernel.** Containers are nothing new; [Solaris Zones](https://en.wikipedia.org/wiki/Solaris_Containers) were released about 20 years ago and the chroot system call was introduced during [development of Version 7 Unix in 1979](https://en.wikipedia.org/wiki/Chroot). It is used ever since for hosting applications exposed to the public Internet. Modern Linux containers are an incremental improvement of this, based on standard functionality that is part of the kernel.
|
||||
|
||||
Human-readable and versioned Dockerfiles as part of our public source code also help avoid "works for me" moments and
|
||||
other unwelcome surprises by enabling teams to have the exact same environment everywhere in
|
||||
[development](https://github.com/photoprism/photoprism/blob/develop/docker/develop/bookworm/Dockerfile), staging,
|
||||
and [production](https://github.com/photoprism/photoprism/blob/develop/docker/photoprism/bookworm/Dockerfile).
|
||||
**(2) Docker saves time through simplified deployment and testing.** A main advantage of Docker is that application images can be [easily made available](https://hub.docker.com/r/photoprism/photoprism) to users via Internet. It provides a common standard across most operating systems and devices, which saves our team a lot of time that we can then spend [more effectively](https://docs.photoprism.app/developer-guide/code-quality/#effectiveness-efficiency), for example, providing support and developing one of the many features that users are waiting for.
|
||||
|
||||
Last but not least, virtually all file format parsers have vulnerabilities that just haven't been discovered yet.
|
||||
This is a known risk that can affect you even if your computer is not directly connected to the Internet.
|
||||
Running apps in a container with limited host access is an easy way to improve security without
|
||||
compromising performance and usability.
|
||||
**(3) Dockerfiles are part of the source code repository.** [Human-readable](https://docs.docker.com/engine/reference/builder/) and [versioned Dockerfiles](https://github.com/photoprism/photoprism/tree/develop/docker) that are part of our public source code help avoid "works for me" moments and other unwelcome surprises by enabling us to have the exact [same environment](http://localhost:8000/developer-guide/setup/) everywhere in [development](https://github.com/photoprism/photoprism/tree/develop/docker/develop), [staging, and production](https://github.com/photoprism/photoprism/tree/develop/docker/photoprism).
|
||||
|
||||
## What about Virtual Machines? ##
|
||||
**(4) Running applications in containers is more secure.** Last but not least, virtually all file format parsers have vulnerabilities that just haven't been discovered yet. This is a known risk that can affect you even if your computer is not directly connected to the Internet. Running apps in a container with limited host access is an easy way to improve security without compromising performance and usability.
|
||||
|
||||
A virtual machine running its own operating system provides more security, but typically has side effects
|
||||
such as lower performance and more difficult handling. You can also run Docker in a VM to get the best of
|
||||
both worlds. It's essentially what happens when you run dockerized applications on [virtual cloud servers](https://docs.photoprism.app/getting-started/cloud/digitalocean/)
|
||||
and operating systems other than Linux.
|
||||
## Why not use virtual machines instead? ##
|
||||
|
||||
## Alternatives ##
|
||||
|
||||
### Building From Source ###
|
||||
|
||||
You can build and install PhotoPrism from the publicly available [source code](https://docs.photoprism.app/developer-guide/setup/):
|
||||
|
||||
```bash
|
||||
git clone https://github.com/photoprism/photoprism.git
|
||||
cd photoprism
|
||||
make all install DESTDIR=/opt/photoprism
|
||||
```
|
||||
|
||||
Missing build dependencies must be installed manually as shown in our human-readable and versioned
|
||||
[Dockerfile](https://github.com/photoprism/photoprism/blob/develop/docker/develop/Dockerfile). You often don't
|
||||
need to use the exact same versions, so it's possible to replace packages with what is available in your environment.
|
||||
|
||||
Please note that we do not have the resources to provide private users with dependencies and
|
||||
[TensorFlow libraries](https://dl.photoprism.app/tensorflow/) for their personal environments. We recommend giving
|
||||
Docker a try if you use Linux as it saves developers a lot of time when building, testing, and deploying complex
|
||||
applications like PhotoPrism. It also effectively helps avoid "works for me" moments and missing dependencies.
|
||||
|
||||
### Installation Packages ###
|
||||
|
||||
An [unofficial port](https://docs.photoprism.app/getting-started/freebsd/) is available for FreeBSD / FreeNAS users.
|
||||
You are invited to contribute by [building and testing standalone packages](https://docs.photoprism.app/developer-guide/) for Linux distributions and other operating systems.
|
||||
|
||||
Updates are [released several times a month](https://docs.photoprism.app/release-notes/), so maintaining the long list of dependencies for additional environments would currently consume too many of [our resources](https://link.photoprism.app/membership).
|
||||
A virtual machine with a dedicated operating system environment provides even more security, but usually has side effects such as lower performance and more difficult handling. Using a VM, however, doesn't prevent you from running containerized apps to get the best of both worlds. This is essentially what happens when you install Docker on [virtual cloud servers](https://docs.photoprism.app/getting-started/cloud/digitalocean/) and operating systems other than Linux.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#### Base Image: Ubuntu 22.04 LTS (Jammy Jellyfish)
|
||||
FROM ubuntu:jammy
|
||||
#### Base Image: Ubuntu 23.10 (Mantic Minotaur)
|
||||
FROM ubuntu:mantic
|
||||
|
||||
# Copyright © 2018 - 2023 PhotoPrism UG. All rights reserved.
|
||||
#
|
||||
|
@ -9,7 +9,7 @@ FROM ubuntu:jammy
|
|||
# Add Open Container Initiative (OCI) annotations.
|
||||
# See: https://github.com/opencontainers/image-spec/blob/main/annotations.md
|
||||
LABEL org.opencontainers.image.title="PhotoPrism® Build Image (ARMv7)"
|
||||
LABEL org.opencontainers.image.description="Ubuntu 22.04 LTS (Jammy Jellyfish)"
|
||||
LABEL org.opencontainers.image.description="Ubuntu 23.10 (Mantic Minotaur)"
|
||||
LABEL org.opencontainers.image.url="https://hub.docker.com/repository/docker/photoprism/develop"
|
||||
LABEL org.opencontainers.image.source="https://github.com/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.documentation="https://docs.photoprism.app/developer-guide/setup/"
|
||||
|
@ -48,19 +48,19 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
|
|||
echo 'APT::Get::Assume-Yes "true";' > /etc/apt/apt.conf.d/80forceyes && \
|
||||
echo 'APT::Get::Fix-Missing "true";' > /etc/apt/apt.conf.d/80fixmissing && \
|
||||
echo 'force-confold' > /etc/dpkg/dpkg.cfg.d/force-confold && \
|
||||
apt-get update && apt-get -qq upgrade && apt-get -qq install --no-install-recommends \
|
||||
apt-get update && apt-get -qq dist-upgrade && apt-get -qq install --no-install-recommends \
|
||||
apt-utils gpg pkg-config software-properties-common ca-certificates avahi-utils \
|
||||
build-essential gcc g++ sudo bash make nano lsof lshw git jq \
|
||||
autoconf automake cmake libtool libjpeg8 libjpeg8-dev \
|
||||
autoconf automake cmake libtool libjpeg-dev libpng-dev libwebp-dev \
|
||||
zip unzip wget curl rsync sqlite3 chrpath gettext libc6-dev \
|
||||
libssl-dev libxft-dev libfreetype6 libfreetype6-dev libfontconfig1 \
|
||||
libfontconfig1-dev libhdf5-serial-dev libpng-dev libzmq3-dev \
|
||||
libfontconfig1-dev libhdf5-serial-dev libzmq3-dev \
|
||||
libx264-dev libx265-dev libde265-dev libaom-dev libnss3 libxtst6 librsvg2-bin tzdata \
|
||||
exiftool ffmpeg libavcodec-extra \
|
||||
&& \
|
||||
/scripts/install-nodejs.sh && \
|
||||
/scripts/install-tensorflow.sh && \
|
||||
/scripts/install-libheif.sh && \
|
||||
/scripts/install-tensorflow.sh && \
|
||||
/scripts/install-go.sh && \
|
||||
/scripts/install-go-tools.sh && \
|
||||
echo 'alias ll="ls -alh"' >> /etc/skel/.bashrc && \
|
||||
|
@ -79,14 +79,14 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
|
|||
/photoprism/storage/cache && \
|
||||
/scripts/cleanup.sh
|
||||
|
||||
# download models and testdata
|
||||
# Download models and testdata.
|
||||
RUN mkdir /tmp/photoprism && \
|
||||
wget "https://dl.photoprism.app/tensorflow/nsfw.zip?${BUILD_TAG}" -O /tmp/photoprism/nsfw.zip && \
|
||||
wget "https://dl.photoprism.app/tensorflow/nasnet.zip?${BUILD_TAG}" -O /tmp/photoprism/nasnet.zip && \
|
||||
wget "https://dl.photoprism.app/tensorflow/facenet.zip?${BUILD_TAG}" -O /tmp/photoprism/facenet.zip && \
|
||||
wget "https://dl.photoprism.app/qa/testdata.zip?${BUILD_TAG}" -O /tmp/photoprism/testdata.zip
|
||||
|
||||
# set up project directory
|
||||
# Default working directory.
|
||||
WORKDIR "/go/src/github.com/photoprism/photoprism"
|
||||
|
||||
# Expose the following container ports:
|
||||
|
@ -103,3 +103,4 @@ ENTRYPOINT ["/scripts/entrypoint.sh"]
|
|||
|
||||
# Keep container running.
|
||||
CMD ["tail", "-f", "/dev/null"]
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
|
|||
echo 'APT::Get::Assume-Yes "true";' > /etc/apt/apt.conf.d/80forceyes && \
|
||||
echo 'APT::Get::Fix-Missing "true";' > /etc/apt/apt.conf.d/80fixmissing && \
|
||||
echo 'force-confold' > /etc/dpkg/dpkg.cfg.d/force-confold && \
|
||||
apt-get update && apt-get -qq upgrade && \
|
||||
apt-get update && apt-get -qq dist-upgrade && \
|
||||
apt-get -qq install \
|
||||
libc6 ca-certificates sudo bash tzdata \
|
||||
gpg zip unzip wget curl rsync make nano \
|
||||
|
|
|
@ -49,7 +49,7 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
|
|||
echo 'APT::Get::Assume-Yes "true";' > /etc/apt/apt.conf.d/80forceyes && \
|
||||
echo 'APT::Get::Fix-Missing "true";' > /etc/apt/apt.conf.d/80fixmissing && \
|
||||
echo 'force-confold' > /etc/dpkg/dpkg.cfg.d/force-confold && \
|
||||
apt-get update && apt-get -qq upgrade && \
|
||||
apt-get update && apt-get -qq dist-upgrade && \
|
||||
apt-get -qq install \
|
||||
libc6 ca-certificates sudo bash tzdata \
|
||||
gpg zip unzip wget curl rsync make nano \
|
||||
|
|
|
@ -42,7 +42,7 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
|
|||
echo 'APT::Get::Assume-Yes "true";' > /etc/apt/apt.conf.d/80forceyes && \
|
||||
echo 'APT::Get::Fix-Missing "true";' > /etc/apt/apt.conf.d/80fixmissing && \
|
||||
echo 'force-confold' > /etc/dpkg/dpkg.cfg.d/force-confold && \
|
||||
apt-get update && apt-get -qq upgrade && \
|
||||
apt-get update && apt-get -qq dist-upgrade && \
|
||||
apt-get -qq install \
|
||||
libc6 ca-certificates bash sudo nano avahi-utils jq lsof lshw \
|
||||
exiftool sqlite3 tzdata gpg make zip unzip wget curl rsync \
|
||||
|
|
|
@ -48,7 +48,7 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
|
|||
echo 'APT::Get::Assume-Yes "true";' > /etc/apt/apt.conf.d/80forceyes && \
|
||||
echo 'APT::Get::Fix-Missing "true";' > /etc/apt/apt.conf.d/80fixmissing && \
|
||||
echo 'force-confold' > /etc/dpkg/dpkg.cfg.d/force-confold && \
|
||||
apt-get update && apt-get -qq upgrade && \
|
||||
apt-get update && apt-get -qq dist-upgrade && \
|
||||
apt-get -qq install \
|
||||
libc6 ca-certificates bash sudo nano avahi-utils jq lsof lshw \
|
||||
exiftool sqlite3 tzdata gpg make zip unzip wget curl rsync \
|
||||
|
@ -58,11 +58,11 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
|
|||
apt-get -qq install \
|
||||
apt-utils pkg-config software-properties-common \
|
||||
build-essential gcc g++ git gettext davfs2 chrpath apache2-utils \
|
||||
autoconf automake cmake libtool libjpeg8-dev \
|
||||
libx264-dev libx265-dev libaom-dev libvpx-dev libwebm-dev libpng-dev libxft-dev \
|
||||
autoconf automake cmake libtool libjpeg-dev libpng-dev libwebp-dev \
|
||||
libx264-dev libx265-dev libaom-dev libvpx-dev libwebm-dev libxft-dev \
|
||||
libc6-dev libhdf5-serial-dev libzmq3-dev libssl-dev libnss3 \
|
||||
libfreetype6 libfreetype6-dev libfontconfig1 libfontconfig1-dev fonts-roboto \
|
||||
librsvg2-bin ghostscript gsfonts \
|
||||
librsvg2-bin ghostscript gsfonts pdf2svg ps2eps \
|
||||
&& \
|
||||
/scripts/install-nodejs.sh && \
|
||||
/scripts/install-mariadb.sh mariadb-client && \
|
||||
|
|
|
@ -42,12 +42,12 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
|
|||
echo 'APT::Get::Assume-Yes "true";' > /etc/apt/apt.conf.d/80forceyes && \
|
||||
echo 'APT::Get::Fix-Missing "true";' > /etc/apt/apt.conf.d/80fixmissing && \
|
||||
echo 'force-confold' > /etc/dpkg/dpkg.cfg.d/force-confold && \
|
||||
apt-get update && apt-get -qq upgrade && \
|
||||
apt-get update && apt-get -qq dist-upgrade && \
|
||||
apt-get -qq install \
|
||||
libc6 ca-certificates bash sudo nano avahi-utils jq lsof lshw \
|
||||
exiftool sqlite3 tzdata gpg make zip unzip wget curl rsync \
|
||||
imagemagick libvips-dev rawtherapee ffmpeg libavcodec-extra x264 x265 libde265-dev \
|
||||
libaom3 libvpx7 libwebm1 libjpeg8 libmatroska7 libdvdread8 libebml5 libgav1-bin libatomic1 \
|
||||
libc6 ca-certificates bash sudo nano avahi-utils jq lsof lshw libebml5 libgav1-bin libatomic1 \
|
||||
exiftool sqlite3 tzdata gpg make zip unzip wget curl rsync imagemagick libvips-dev rawtherapee \
|
||||
ffmpeg libffmpeg-nvenc-dev libswscale-dev libavfilter-extra libavformat-extra libavcodec-extra \
|
||||
x264 x265 libde265-dev libaom3 libvpx7 libwebm1 libjpeg8 libmatroska7 libdvdread8 \
|
||||
&& \
|
||||
/scripts/install-mariadb.sh mariadb-client && \
|
||||
/scripts/install-darktable.sh && \
|
||||
|
@ -75,4 +75,4 @@ WORKDIR /photoprism
|
|||
EXPOSE 2342 2442 2443
|
||||
|
||||
# Keep container running.
|
||||
CMD ["tail", "-f", "/dev/null"]
|
||||
CMD ["tail", "-f", "/dev/null"]
|
||||
|
|
|
@ -48,21 +48,21 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
|
|||
echo 'APT::Get::Assume-Yes "true";' > /etc/apt/apt.conf.d/80forceyes && \
|
||||
echo 'APT::Get::Fix-Missing "true";' > /etc/apt/apt.conf.d/80fixmissing && \
|
||||
echo 'force-confold' > /etc/dpkg/dpkg.cfg.d/force-confold && \
|
||||
apt-get update && apt-get -qq upgrade && \
|
||||
apt-get update && apt-get -qq dist-upgrade && \
|
||||
apt-get -qq install \
|
||||
libc6 ca-certificates bash sudo nano avahi-utils jq lsof lshw \
|
||||
exiftool sqlite3 tzdata gpg make zip unzip wget curl rsync \
|
||||
imagemagick libvips-dev rawtherapee ffmpeg libavcodec-extra x264 x265 libde265-dev \
|
||||
libaom3 libvpx7 libwebm1 libjpeg8 libmatroska7 libdvdread8 libebml5 libgav1-bin libatomic1 \
|
||||
libc6 ca-certificates bash sudo nano avahi-utils jq lsof lshw libebml5 libgav1-bin libatomic1 \
|
||||
exiftool sqlite3 tzdata gpg make zip unzip wget curl rsync imagemagick libvips-dev rawtherapee \
|
||||
ffmpeg libffmpeg-nvenc-dev libswscale-dev libavfilter-extra libavformat-extra libavcodec-extra \
|
||||
x264 x265 libde265-dev libaom3 libvpx7 libwebm1 libjpeg8 libmatroska7 libdvdread8 \
|
||||
&& \
|
||||
apt-get -qq install \
|
||||
apt-utils pkg-config software-properties-common \
|
||||
build-essential gcc g++ git gettext davfs2 chrpath apache2-utils \
|
||||
autoconf automake cmake libtool libjpeg8-dev \
|
||||
libx264-dev libx265-dev libaom-dev libvpx-dev libwebm-dev libpng-dev libxft-dev \
|
||||
autoconf automake cmake libtool libjpeg-dev libpng-dev libwebp-dev \
|
||||
libx264-dev libx265-dev libaom-dev libvpx-dev libwebm-dev libxft-dev \
|
||||
libc6-dev libhdf5-serial-dev libzmq3-dev libssl-dev libnss3 \
|
||||
libfreetype6 libfreetype6-dev libfontconfig1 libfontconfig1-dev fonts-roboto \
|
||||
librsvg2-bin ghostscript gsfonts \
|
||||
librsvg2-bin ghostscript gsfonts pdf2svg ps2eps \
|
||||
&& \
|
||||
/scripts/install-nodejs.sh && \
|
||||
/scripts/install-mariadb.sh mariadb-client && \
|
||||
|
|
78
docker/develop/mantic-slim/Dockerfile
Normal file
78
docker/develop/mantic-slim/Dockerfile
Normal file
|
@ -0,0 +1,78 @@
|
|||
#### Base Image: Ubuntu 23.10 (Mantic Minotaur)
|
||||
FROM ubuntu:mantic
|
||||
|
||||
# Copyright © 2018 - 2023 PhotoPrism UG. All rights reserved.
|
||||
#
|
||||
# Questions? Email us at hello@photoprism.app or visit our website to learn
|
||||
# more about our team, products and services: https://www.photoprism.app/
|
||||
|
||||
# Add Open Container Initiative (OCI) annotations.
|
||||
# See: https://github.com/opencontainers/image-spec/blob/main/annotations.md
|
||||
LABEL org.opencontainers.image.title="PhotoPrism® Base Image (Ubuntu 23.10)"
|
||||
LABEL org.opencontainers.image.description="Ubuntu 23.10 (Mantic Minotaur)"
|
||||
LABEL org.opencontainers.image.url="https://hub.docker.com/repository/docker/photoprism/develop"
|
||||
LABEL org.opencontainers.image.source="https://github.com/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.documentation="https://docs.photoprism.app/developer-guide/setup/"
|
||||
LABEL org.opencontainers.image.authors="PhotoPrism UG <hello@photoprism.app>"
|
||||
LABEL org.opencontainers.image.vendor="PhotoPrism UG"
|
||||
|
||||
# Declare build parameters.
|
||||
ARG TARGETARCH
|
||||
ARG BUILD_TAG
|
||||
|
||||
# Set environment variables, see https://docs.photoprism.app/getting-started/config-options/.
|
||||
ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
||||
DOCKER_TAG=$BUILD_TAG \
|
||||
DOCKER_ENV="prod" \
|
||||
PS1="\u@$DOCKER_TAG:\w\$ " \
|
||||
PATH="/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin:/scripts:/opt/photoprism/bin" \
|
||||
LD_LIBRARY_PATH="/usr/local/lib:/usr/lib" \
|
||||
TMPDIR="/tmp" \
|
||||
DEBIAN_FRONTEND="noninteractive" \
|
||||
TF_CPP_MIN_LOG_LEVEL="2" \
|
||||
PROG="photoprism"
|
||||
|
||||
# Copy scripts and package sources config.
|
||||
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/
|
||||
|
||||
# Update base image and add dependencies.
|
||||
RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
|
||||
echo 'APT::Install-Recommends "false";' > /etc/apt/apt.conf.d/80recommends && \
|
||||
echo 'APT::Install-Suggests "false";' > /etc/apt/apt.conf.d/80suggests && \
|
||||
echo 'APT::Get::Assume-Yes "true";' > /etc/apt/apt.conf.d/80forceyes && \
|
||||
echo 'APT::Get::Fix-Missing "true";' > /etc/apt/apt.conf.d/80fixmissing && \
|
||||
echo 'force-confold' > /etc/dpkg/dpkg.cfg.d/force-confold && \
|
||||
apt-get update && apt-get -qq dist-upgrade && \
|
||||
apt-get -qq install \
|
||||
libc6 ca-certificates bash sudo nano avahi-utils jq lsof lshw libebml5 libgav1-bin libatomic1 \
|
||||
exiftool sqlite3 tzdata gpg make zip unzip wget curl rsync imagemagick libvips-dev rawtherapee \
|
||||
ffmpeg libffmpeg-nvenc-dev libswscale-dev libavfilter-extra libavformat-extra libavcodec-extra \
|
||||
x264 x265 libde265-dev libaom3 libvpx7 libwebm1 libjpeg8 libmatroska7 libdvdread8 \
|
||||
&& \
|
||||
/scripts/install-mariadb.sh mariadb-client && \
|
||||
/scripts/install-darktable.sh && \
|
||||
/scripts/install-libheif.sh && \
|
||||
echo 'alias ll="ls -alh"' >> /etc/skel/.bashrc && \
|
||||
echo 'export PS1="\u@$DOCKER_TAG:\w\$ "' >> /etc/skel/.bashrc && \
|
||||
echo "ALL ALL=(ALL) NOPASSWD:SETENV: /scripts/entrypoint-init.sh" >> /etc/sudoers.d/init && \
|
||||
cp /etc/skel/.bashrc /root/.bashrc && \
|
||||
/scripts/create-users.sh && \
|
||||
install -d -m 0777 -o 1000 -g 1000 \
|
||||
/photoprism/originals \
|
||||
/photoprism/import \
|
||||
/photoprism/storage \
|
||||
/photoprism/storage/sidecar \
|
||||
/photoprism/storage/albums \
|
||||
/photoprism/storage/backups \
|
||||
/photoprism/storage/config \
|
||||
/photoprism/storage/cache && \
|
||||
/scripts/cleanup.sh
|
||||
|
||||
# Default working directory.
|
||||
WORKDIR /photoprism
|
||||
|
||||
# Expose HTTP and HTTPS ports.
|
||||
EXPOSE 2342 2442 2443
|
||||
|
||||
# Keep container running.
|
||||
CMD ["tail", "-f", "/dev/null"]
|
115
docker/develop/mantic/Dockerfile
Normal file
115
docker/develop/mantic/Dockerfile
Normal file
|
@ -0,0 +1,115 @@
|
|||
#### Base Image: Ubuntu 23.10 (Mantic Minotaur)
|
||||
FROM ubuntu:mantic
|
||||
|
||||
# Copyright © 2018 - 2023 PhotoPrism UG. All rights reserved.
|
||||
#
|
||||
# Questions? Email us at hello@photoprism.app or visit our website to learn
|
||||
# more about our team, products and services: https://www.photoprism.app/
|
||||
|
||||
# Add Open Container Initiative (OCI) annotations.
|
||||
# See: https://github.com/opencontainers/image-spec/blob/main/annotations.md
|
||||
LABEL org.opencontainers.image.title="PhotoPrism® Build Image (Ubuntu 23.10)"
|
||||
LABEL org.opencontainers.image.description="Ubuntu 23.10 (Mantic Minotaur)"
|
||||
LABEL org.opencontainers.image.url="https://hub.docker.com/repository/docker/photoprism/develop"
|
||||
LABEL org.opencontainers.image.source="https://github.com/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.documentation="https://docs.photoprism.app/developer-guide/setup/"
|
||||
LABEL org.opencontainers.image.authors="PhotoPrism UG <hello@photoprism.app>"
|
||||
LABEL org.opencontainers.image.vendor="PhotoPrism UG"
|
||||
|
||||
# Declare build parameters.
|
||||
ARG TARGETARCH
|
||||
ARG BUILD_TAG
|
||||
|
||||
# Set environment variables, see https://docs.photoprism.app/getting-started/config-options/.
|
||||
ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
||||
DOCKER_TAG=$BUILD_TAG \
|
||||
DOCKER_ENV="develop" \
|
||||
NODE_ENV="production" \
|
||||
PS1="\u@$DOCKER_TAG:\w\$ " \
|
||||
PATH="/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin:/scripts:/usr/local/go/bin:/go/bin:/opt/photoprism/bin" \
|
||||
LD_LIBRARY_PATH="/usr/local/lib:/usr/lib" \
|
||||
DEBIAN_FRONTEND="noninteractive" \
|
||||
TMPDIR="/tmp" \
|
||||
TF_CPP_MIN_LOG_LEVEL="0" \
|
||||
GOPATH="/go" \
|
||||
GOBIN="/usr/local/bin" \
|
||||
GO111MODULE="on" \
|
||||
CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \
|
||||
PROG="photoprism"
|
||||
|
||||
# Copy scripts and package sources config.
|
||||
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/
|
||||
COPY --chown=root:root --chmod=644 /.my.cnf /etc/my.cnf
|
||||
|
||||
# Update base image and add dependencies.
|
||||
RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
|
||||
echo 'APT::Install-Recommends "false";' > /etc/apt/apt.conf.d/80recommends && \
|
||||
echo 'APT::Install-Suggests "false";' > /etc/apt/apt.conf.d/80suggests && \
|
||||
echo 'APT::Get::Assume-Yes "true";' > /etc/apt/apt.conf.d/80forceyes && \
|
||||
echo 'APT::Get::Fix-Missing "true";' > /etc/apt/apt.conf.d/80fixmissing && \
|
||||
echo 'force-confold' > /etc/dpkg/dpkg.cfg.d/force-confold && \
|
||||
apt-get update && apt-get -qq dist-upgrade && \
|
||||
apt-get -qq install \
|
||||
libc6 ca-certificates bash sudo nano avahi-utils jq lsof lshw libebml5 libgav1-bin libatomic1 \
|
||||
exiftool sqlite3 tzdata gpg make zip unzip wget curl rsync imagemagick libvips-dev rawtherapee \
|
||||
ffmpeg libffmpeg-nvenc-dev libswscale-dev libavfilter-extra libavformat-extra libavcodec-extra \
|
||||
x264 x265 libde265-dev libaom3 libvpx7 libwebm1 libjpeg8 libmatroska7 libdvdread8 \
|
||||
&& \
|
||||
apt-get -qq install \
|
||||
apt-utils pkg-config software-properties-common \
|
||||
build-essential gcc g++ git gettext davfs2 chrpath apache2-utils \
|
||||
autoconf automake cmake libtool libjpeg-dev libpng-dev libwebp-dev \
|
||||
libx264-dev libx265-dev libaom-dev libvpx-dev libwebm-dev libxft-dev \
|
||||
libc6-dev libhdf5-serial-dev libzmq3-dev libssl-dev libnss3 \
|
||||
libfreetype6 libfreetype6-dev libfontconfig1 libfontconfig1-dev fonts-roboto \
|
||||
librsvg2-bin ghostscript gsfonts pdf2svg ps2eps \
|
||||
&& \
|
||||
/scripts/install-nodejs.sh && \
|
||||
/scripts/install-mariadb.sh mariadb-client && \
|
||||
/scripts/install-tensorflow.sh && \
|
||||
/scripts/install-darktable.sh && \
|
||||
/scripts/install-libheif.sh && \
|
||||
/scripts/install-chrome.sh && \
|
||||
/scripts/install-go.sh && \
|
||||
/scripts/install-go-tools.sh && \
|
||||
echo 'alias go=richgo ll="ls -alh"' >> /etc/skel/.bashrc && \
|
||||
echo 'export PS1="\u@$DOCKER_TAG:\w\$ "' >> /etc/skel/.bashrc && \
|
||||
echo "ALL ALL=(ALL) NOPASSWD:SETENV: ALL" >> /etc/sudoers.d/all && \
|
||||
cp /etc/skel/.bashrc /root/.bashrc && \
|
||||
cp /scripts/convert/policy.xml /etc/ImageMagick-6/policy.xml && \
|
||||
/scripts/create-users.sh && \
|
||||
install -d -m 0777 -o 1000 -g 1000 \
|
||||
/photoprism/originals \
|
||||
/photoprism/import \
|
||||
/photoprism/storage \
|
||||
/photoprism/storage/sidecar \
|
||||
/photoprism/storage/albums \
|
||||
/photoprism/storage/backups \
|
||||
/photoprism/storage/config \
|
||||
/photoprism/storage/cache && \
|
||||
/scripts/cleanup.sh
|
||||
|
||||
# Download models and testdata.
|
||||
RUN mkdir /tmp/photoprism && \
|
||||
wget "https://dl.photoprism.app/tensorflow/nsfw.zip?${BUILD_TAG}" -O /tmp/photoprism/nsfw.zip && \
|
||||
wget "https://dl.photoprism.app/tensorflow/nasnet.zip?${BUILD_TAG}" -O /tmp/photoprism/nasnet.zip && \
|
||||
wget "https://dl.photoprism.app/tensorflow/facenet.zip?${BUILD_TAG}" -O /tmp/photoprism/facenet.zip && \
|
||||
wget "https://dl.photoprism.app/qa/testdata.zip?${BUILD_TAG}" -O /tmp/photoprism/testdata.zip
|
||||
|
||||
# Default working directory.
|
||||
WORKDIR "/go/src/github.com/photoprism/photoprism"
|
||||
|
||||
# Expose the following container ports:
|
||||
# - 2342 (HTTP)
|
||||
# - 2343 (Acceptance Tests)
|
||||
# - 2442 (HTTP)
|
||||
# - 2443 (HTTPS)
|
||||
# - 9515 (Chromedriver)
|
||||
# - 40000 (Go Debugger)
|
||||
EXPOSE 2342 2343 2442 2443 9515 40000
|
||||
|
||||
# Declare container entrypoint script.
|
||||
ENTRYPOINT ["/scripts/entrypoint.sh"]
|
||||
|
||||
# Keep container running.
|
||||
CMD ["tail", "-f", "/dev/null"]
|
|
@ -8,9 +8,10 @@ FROM photoprism/develop:armv7 as build
|
|||
|
||||
# Declare build parameters.
|
||||
ARG TARGETARCH
|
||||
ARG TARGETPLATFORM
|
||||
ARG BUILD_TAG
|
||||
|
||||
# Copy source to image.
|
||||
# Copy project files.
|
||||
WORKDIR "/go/src/github.com/photoprism/photoprism"
|
||||
COPY . .
|
||||
|
||||
|
@ -18,18 +19,19 @@ COPY . .
|
|||
RUN make all install DESTDIR=/opt/photoprism
|
||||
|
||||
################################################## PRODUCTION STAGE ####################################################
|
||||
#### Base Image: Ubuntu 22.04 LTS (Jammy Jellyfish)
|
||||
FROM ubuntu:jammy
|
||||
#### Base Image: Ubuntu 23.10 (Mantic Minotaur)
|
||||
FROM ubuntu:mantic
|
||||
|
||||
# Add Open Container Initiative (OCI) annotations.
|
||||
# See: https://github.com/opencontainers/image-spec/blob/main/annotations.md
|
||||
LABEL org.opencontainers.image.title="PhotoPrism® (ARMv7)"
|
||||
LABEL org.opencontainers.image.description="Ubuntu 22.04 LTS (Jammy Jellyfish)"
|
||||
LABEL org.opencontainers.image.url="https://hub.docker.com/repository/docker/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.description="Ubuntu 23.10 (Mantic Minotaur)"
|
||||
LABEL org.opencontainers.image.url="https://hub.docker.com/r/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.source="https://github.com/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.documentation="https://docs.photoprism.app/getting-started/"
|
||||
LABEL org.opencontainers.image.authors="PhotoPrism UG <hello@photoprism.app>"
|
||||
LABEL org.opencontainers.image.vendor="PhotoPrism UG"
|
||||
LABEL org.opencontainers.image.ref.name="photoprism"
|
||||
|
||||
# Declare build parameters.
|
||||
ARG TARGETARCH
|
||||
|
@ -59,7 +61,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
|||
PHOTOPRISM_UPLOAD_NSFW="true" \
|
||||
PHOTOPRISM_DETECT_NSFW="false" \
|
||||
PHOTOPRISM_EXPERIMENTAL="false" \
|
||||
PHOTOPRISM_SITE_URL="http://photoprism.me:2342/" \
|
||||
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
||||
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
||||
PHOTOPRISM_SITE_DESCRIPTION="" \
|
||||
PHOTOPRISM_SITE_AUTHOR="" \
|
||||
|
@ -89,7 +91,8 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
|||
PHOTOPRISM_WORKERS=0 \
|
||||
PHOTOPRISM_WAKEUP_INTERVAL=900 \
|
||||
PHOTOPRISM_AUTO_INDEX=300 \
|
||||
PHOTOPRISM_AUTO_IMPORT=300
|
||||
PHOTOPRISM_AUTO_IMPORT=300 \
|
||||
PHOTOPRISM_INIT="https"
|
||||
|
||||
# Copy dist files, scripts, and debian backports sources list.
|
||||
COPY --from=build --chown=root:root --chmod=755 /opt/photoprism/ /opt/photoprism
|
||||
|
@ -102,7 +105,7 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
|
|||
echo 'APT::Get::Assume-Yes "true";' > /etc/apt/apt.conf.d/80forceyes && \
|
||||
echo 'APT::Get::Fix-Missing "true";' > /etc/apt/apt.conf.d/80fixmissing && \
|
||||
echo 'force-confold' > /etc/dpkg/dpkg.cfg.d/force-confold && \
|
||||
apt-get update && apt-get -qq upgrade && \
|
||||
apt-get update && apt-get -qq dist-upgrade && \
|
||||
apt-get -qq install --no-install-recommends \
|
||||
libc6 ca-certificates bash sudo nano avahi-utils jq lsof lshw \
|
||||
exiftool mariadb-client sqlite3 tzdata gpg make zip unzip wget curl rsync \
|
||||
|
|
|
@ -25,7 +25,7 @@ FROM photoprism/develop:bookworm-slim
|
|||
# See: https://github.com/opencontainers/image-spec/blob/main/annotations.md
|
||||
LABEL org.opencontainers.image.title="PhotoPrism® (Debian 12)"
|
||||
LABEL org.opencontainers.image.description="Debian 12 (Bookworm)"
|
||||
LABEL org.opencontainers.image.url="https://hub.docker.com/repository/docker/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.url="https://hub.docker.com/r/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.source="https://github.com/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.documentation="https://docs.photoprism.app/getting-started/"
|
||||
LABEL org.opencontainers.image.authors="PhotoPrism UG <hello@photoprism.app>"
|
||||
|
@ -56,7 +56,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
|||
PHOTOPRISM_UPLOAD_NSFW="true" \
|
||||
PHOTOPRISM_DETECT_NSFW="false" \
|
||||
PHOTOPRISM_EXPERIMENTAL="false" \
|
||||
PHOTOPRISM_SITE_URL="http://photoprism.me:2342/" \
|
||||
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
||||
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
||||
PHOTOPRISM_SITE_DESCRIPTION="" \
|
||||
PHOTOPRISM_SITE_AUTHOR="" \
|
||||
|
@ -86,14 +86,15 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
|||
PHOTOPRISM_WORKERS=0 \
|
||||
PHOTOPRISM_WAKEUP_INTERVAL=900 \
|
||||
PHOTOPRISM_AUTO_INDEX=300 \
|
||||
PHOTOPRISM_AUTO_IMPORT=300
|
||||
PHOTOPRISM_AUTO_IMPORT=300 \
|
||||
PHOTOPRISM_INIT="https"
|
||||
|
||||
# Copy scripts.
|
||||
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/
|
||||
|
||||
# Update pre-installed packages.
|
||||
RUN apt-get update && \
|
||||
apt-get -qq upgrade && \
|
||||
apt-get -qq dist-upgrade && \
|
||||
/scripts/cleanup.sh
|
||||
|
||||
# Default working directory.
|
||||
|
|
|
@ -25,7 +25,7 @@ FROM photoprism/develop:bullseye-slim
|
|||
# See: https://github.com/opencontainers/image-spec/blob/main/annotations.md
|
||||
LABEL org.opencontainers.image.title="PhotoPrism® (Debian 11)"
|
||||
LABEL org.opencontainers.image.description="Debian 11 (Bullseye)"
|
||||
LABEL org.opencontainers.image.url="https://hub.docker.com/repository/docker/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.url="https://hub.docker.com/r/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.source="https://github.com/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.documentation="https://docs.photoprism.app/getting-started/"
|
||||
LABEL org.opencontainers.image.authors="PhotoPrism UG <hello@photoprism.app>"
|
||||
|
@ -56,7 +56,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
|||
PHOTOPRISM_UPLOAD_NSFW="true" \
|
||||
PHOTOPRISM_DETECT_NSFW="false" \
|
||||
PHOTOPRISM_EXPERIMENTAL="false" \
|
||||
PHOTOPRISM_SITE_URL="http://photoprism.me:2342/" \
|
||||
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
||||
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
||||
PHOTOPRISM_SITE_DESCRIPTION="" \
|
||||
PHOTOPRISM_SITE_AUTHOR="" \
|
||||
|
@ -86,7 +86,8 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
|||
PHOTOPRISM_WORKERS=0 \
|
||||
PHOTOPRISM_WAKEUP_INTERVAL=900 \
|
||||
PHOTOPRISM_AUTO_INDEX=300 \
|
||||
PHOTOPRISM_AUTO_IMPORT=300
|
||||
PHOTOPRISM_AUTO_IMPORT=300 \
|
||||
PHOTOPRISM_INIT="https"
|
||||
|
||||
# Copy scripts.
|
||||
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/
|
||||
|
|
|
@ -25,7 +25,7 @@ FROM debian:buster-slim
|
|||
# See: https://github.com/opencontainers/image-spec/blob/main/annotations.md
|
||||
LABEL org.opencontainers.image.title="PhotoPrism® (Debian 10)"
|
||||
LABEL org.opencontainers.image.description="Debian 10 (Buster)"
|
||||
LABEL org.opencontainers.image.url="https://hub.docker.com/repository/docker/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.url="https://hub.docker.com/r/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.source="https://github.com/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.documentation="https://docs.photoprism.app/getting-started/"
|
||||
LABEL org.opencontainers.image.authors="PhotoPrism UG <hello@photoprism.app>"
|
||||
|
@ -59,7 +59,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
|||
PHOTOPRISM_UPLOAD_NSFW="true" \
|
||||
PHOTOPRISM_DETECT_NSFW="false" \
|
||||
PHOTOPRISM_EXPERIMENTAL="false" \
|
||||
PHOTOPRISM_SITE_URL="http://photoprism.me:2342/" \
|
||||
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
||||
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
||||
PHOTOPRISM_SITE_DESCRIPTION="" \
|
||||
PHOTOPRISM_SITE_AUTHOR="" \
|
||||
|
@ -89,7 +89,8 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
|||
PHOTOPRISM_WORKERS=0 \
|
||||
PHOTOPRISM_WAKEUP_INTERVAL=900 \
|
||||
PHOTOPRISM_AUTO_INDEX=300 \
|
||||
PHOTOPRISM_AUTO_IMPORT=300
|
||||
PHOTOPRISM_AUTO_IMPORT=300 \
|
||||
PHOTOPRISM_INIT="https"
|
||||
|
||||
# Copy dist files, scripts, and debian backports sources list.
|
||||
COPY --from=build --chown=root:root --chmod=755 /opt/photoprism/ /opt/photoprism
|
||||
|
|
|
@ -25,7 +25,7 @@ FROM ubuntu:impish
|
|||
# See: https://github.com/opencontainers/image-spec/blob/main/annotations.md
|
||||
LABEL org.opencontainers.image.title="PhotoPrism® (Ubuntu 21.10)"
|
||||
LABEL org.opencontainers.image.description="Ubuntu 21.10 (Impish Indri)"
|
||||
LABEL org.opencontainers.image.url="https://hub.docker.com/repository/docker/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.url="https://hub.docker.com/r/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.source="https://github.com/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.documentation="https://docs.photoprism.app/getting-started/"
|
||||
LABEL org.opencontainers.image.authors="PhotoPrism UG <hello@photoprism.app>"
|
||||
|
@ -59,7 +59,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
|||
PHOTOPRISM_UPLOAD_NSFW="true" \
|
||||
PHOTOPRISM_DETECT_NSFW="false" \
|
||||
PHOTOPRISM_EXPERIMENTAL="false" \
|
||||
PHOTOPRISM_SITE_URL="http://photoprism.me:2342/" \
|
||||
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
||||
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
||||
PHOTOPRISM_SITE_DESCRIPTION="" \
|
||||
PHOTOPRISM_SITE_AUTHOR="" \
|
||||
|
@ -89,7 +89,8 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
|||
PHOTOPRISM_WORKERS=0 \
|
||||
PHOTOPRISM_WAKEUP_INTERVAL=900 \
|
||||
PHOTOPRISM_AUTO_INDEX=300 \
|
||||
PHOTOPRISM_AUTO_IMPORT=300
|
||||
PHOTOPRISM_AUTO_IMPORT=300 \
|
||||
PHOTOPRISM_INIT="https"
|
||||
|
||||
# Copy dist files and scripts.
|
||||
COPY --from=build --chown=root:root --chmod=755 /opt/photoprism/ /opt/photoprism
|
||||
|
|
|
@ -26,7 +26,7 @@ FROM photoprism/develop:jammy-slim
|
|||
# See: https://github.com/opencontainers/image-spec/blob/main/annotations.md
|
||||
LABEL org.opencontainers.image.title="PhotoPrism® (Ubuntu 22.04 LTS)"
|
||||
LABEL org.opencontainers.image.description="Ubuntu 22.04 LTS (Jammy Jellyfish)"
|
||||
LABEL org.opencontainers.image.url="https://hub.docker.com/repository/docker/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.url="https://hub.docker.com/r/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.source="https://github.com/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.documentation="https://docs.photoprism.app/getting-started/"
|
||||
LABEL org.opencontainers.image.authors="PhotoPrism UG <hello@photoprism.app>"
|
||||
|
@ -57,7 +57,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
|||
PHOTOPRISM_UPLOAD_NSFW="true" \
|
||||
PHOTOPRISM_DETECT_NSFW="false" \
|
||||
PHOTOPRISM_EXPERIMENTAL="false" \
|
||||
PHOTOPRISM_SITE_URL="http://photoprism.me:2342/" \
|
||||
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
||||
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
||||
PHOTOPRISM_SITE_DESCRIPTION="" \
|
||||
PHOTOPRISM_SITE_AUTHOR="" \
|
||||
|
@ -87,14 +87,15 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
|||
PHOTOPRISM_WORKERS=0 \
|
||||
PHOTOPRISM_WAKEUP_INTERVAL=900 \
|
||||
PHOTOPRISM_AUTO_INDEX=300 \
|
||||
PHOTOPRISM_AUTO_IMPORT=300
|
||||
PHOTOPRISM_AUTO_IMPORT=300 \
|
||||
PHOTOPRISM_INIT="https"
|
||||
|
||||
# Copy scripts.
|
||||
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/
|
||||
|
||||
# Update pre-installed packages.
|
||||
RUN apt-get update && \
|
||||
apt-get -qq upgrade && \
|
||||
apt-get -qq dist-upgrade && \
|
||||
/scripts/cleanup.sh
|
||||
|
||||
# Default working directory.
|
||||
|
|
|
@ -26,7 +26,7 @@ FROM photoprism/develop:lunar-slim
|
|||
# See: https://github.com/opencontainers/image-spec/blob/main/annotations.md
|
||||
LABEL org.opencontainers.image.title="PhotoPrism® (Ubuntu 23.04)"
|
||||
LABEL org.opencontainers.image.description="Ubuntu 23.04 (Lunar Lobster)"
|
||||
LABEL org.opencontainers.image.url="https://hub.docker.com/repository/docker/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.url="https://hub.docker.com/r/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.source="https://github.com/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.documentation="https://docs.photoprism.app/getting-started/"
|
||||
LABEL org.opencontainers.image.authors="PhotoPrism UG <hello@photoprism.app>"
|
||||
|
@ -57,7 +57,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
|||
PHOTOPRISM_UPLOAD_NSFW="true" \
|
||||
PHOTOPRISM_DETECT_NSFW="false" \
|
||||
PHOTOPRISM_EXPERIMENTAL="false" \
|
||||
PHOTOPRISM_SITE_URL="http://photoprism.me:2342/" \
|
||||
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
||||
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
||||
PHOTOPRISM_SITE_DESCRIPTION="" \
|
||||
PHOTOPRISM_SITE_AUTHOR="" \
|
||||
|
@ -87,14 +87,15 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
|||
PHOTOPRISM_WORKERS=0 \
|
||||
PHOTOPRISM_WAKEUP_INTERVAL=900 \
|
||||
PHOTOPRISM_AUTO_INDEX=300 \
|
||||
PHOTOPRISM_AUTO_IMPORT=300
|
||||
PHOTOPRISM_AUTO_IMPORT=300 \
|
||||
PHOTOPRISM_INIT="https"
|
||||
|
||||
# Copy scripts.
|
||||
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/
|
||||
|
||||
# Update pre-installed packages.
|
||||
RUN apt-get update && \
|
||||
apt-get -qq upgrade && \
|
||||
apt-get -qq dist-upgrade && \
|
||||
/scripts/cleanup.sh
|
||||
|
||||
# Default working directory.
|
||||
|
|
114
docker/photoprism/mantic/Dockerfile
Normal file
114
docker/photoprism/mantic/Dockerfile
Normal file
|
@ -0,0 +1,114 @@
|
|||
##################################################### BUILD STAGE ######################################################
|
||||
FROM photoprism/develop:mantic as build
|
||||
|
||||
# Copyright © 2018 - 2023 PhotoPrism UG. All rights reserved.
|
||||
#
|
||||
# Questions? Email us at hello@photoprism.app or visit our website to learn
|
||||
# more about our team, products and services: https://www.photoprism.app/
|
||||
|
||||
# Declare build parameters.
|
||||
ARG TARGETARCH
|
||||
ARG TARGETPLATFORM
|
||||
ARG BUILD_TAG
|
||||
|
||||
# Copy source to image.
|
||||
WORKDIR "/go/src/github.com/photoprism/photoprism"
|
||||
COPY . .
|
||||
|
||||
# Build app.
|
||||
RUN make all install DESTDIR=/opt/photoprism
|
||||
|
||||
################################################## PRODUCTION STAGE ####################################################
|
||||
#### Base Image: Ubuntu 23.10 (Mantic Minotaur)
|
||||
FROM photoprism/develop:mantic-slim
|
||||
|
||||
# Add Open Container Initiative (OCI) annotations.
|
||||
# See: https://github.com/opencontainers/image-spec/blob/main/annotations.md
|
||||
LABEL org.opencontainers.image.title="PhotoPrism® (Ubuntu 23.10)"
|
||||
LABEL org.opencontainers.image.description="Ubuntu 23.10 (Mantic Minotaur)"
|
||||
LABEL org.opencontainers.image.url="https://hub.docker.com/r/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.source="https://github.com/photoprism/photoprism"
|
||||
LABEL org.opencontainers.image.documentation="https://docs.photoprism.app/getting-started/"
|
||||
LABEL org.opencontainers.image.authors="PhotoPrism UG <hello@photoprism.app>"
|
||||
LABEL org.opencontainers.image.vendor="PhotoPrism UG"
|
||||
|
||||
# Declare build parameters.
|
||||
ARG TARGETARCH
|
||||
ARG BUILD_TAG
|
||||
|
||||
# Set environment variables, see https://docs.photoprism.app/getting-started/config-options/.
|
||||
ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
||||
DOCKER_TAG=$BUILD_TAG \
|
||||
DOCKER_ENV="prod" \
|
||||
TMPDIR="/tmp" \
|
||||
DEBIAN_FRONTEND="noninteractive" \
|
||||
TF_CPP_MIN_LOG_LEVEL="2" \
|
||||
PROG="photoprism" \
|
||||
PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \
|
||||
PHOTOPRISM_IMPORT_PATH="/photoprism/import" \
|
||||
PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \
|
||||
PHOTOPRISM_STORAGE_PATH="/photoprism/storage" \
|
||||
PHOTOPRISM_BACKUP_PATH="/photoprism/storage/backups" \
|
||||
PHOTOPRISM_LOG_FILENAME="/photoprism/storage/photoprism.log" \
|
||||
PHOTOPRISM_PID_FILENAME="/photoprism/storage/photoprism.pid" \
|
||||
PHOTOPRISM_DEBUG="false" \
|
||||
PHOTOPRISM_PUBLIC="false" \
|
||||
PHOTOPRISM_READONLY="false" \
|
||||
PHOTOPRISM_UPLOAD_NSFW="true" \
|
||||
PHOTOPRISM_DETECT_NSFW="false" \
|
||||
PHOTOPRISM_EXPERIMENTAL="false" \
|
||||
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
||||
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
||||
PHOTOPRISM_SITE_DESCRIPTION="" \
|
||||
PHOTOPRISM_SITE_AUTHOR="" \
|
||||
PHOTOPRISM_HTTP_HOST="0.0.0.0" \
|
||||
PHOTOPRISM_HTTP_PORT=2342 \
|
||||
PHOTOPRISM_DATABASE_DRIVER="sqlite" \
|
||||
PHOTOPRISM_DATABASE_SERVER="" \
|
||||
PHOTOPRISM_DATABASE_NAME="photoprism" \
|
||||
PHOTOPRISM_DATABASE_USER="photoprism" \
|
||||
PHOTOPRISM_DATABASE_PASSWORD="" \
|
||||
PHOTOPRISM_DISABLE_CHOWN="false" \
|
||||
PHOTOPRISM_DISABLE_WEBDAV="false" \
|
||||
PHOTOPRISM_DISABLE_SETTINGS="false" \
|
||||
PHOTOPRISM_DISABLE_BACKUPS="false" \
|
||||
PHOTOPRISM_DISABLE_EXIFTOOL="false" \
|
||||
PHOTOPRISM_DISABLE_PLACES="false" \
|
||||
PHOTOPRISM_DISABLE_TENSORFLOW="false" \
|
||||
PHOTOPRISM_DISABLE_FACES="false" \
|
||||
PHOTOPRISM_DISABLE_CLASSIFICATION="false" \
|
||||
PHOTOPRISM_RAW_PRESETS="false" \
|
||||
PHOTOPRISM_THUMB_FILTER="lanczos" \
|
||||
PHOTOPRISM_THUMB_UNCACHED="false" \
|
||||
PHOTOPRISM_THUMB_SIZE=2048 \
|
||||
PHOTOPRISM_THUMB_SIZE_UNCACHED=7680 \
|
||||
PHOTOPRISM_JPEG_SIZE=7680 \
|
||||
PHOTOPRISM_JPEG_QUALITY=85 \
|
||||
PHOTOPRISM_WORKERS=0 \
|
||||
PHOTOPRISM_WAKEUP_INTERVAL=900 \
|
||||
PHOTOPRISM_AUTO_INDEX=300 \
|
||||
PHOTOPRISM_AUTO_IMPORT=300 \
|
||||
PHOTOPRISM_INIT="https"
|
||||
|
||||
# Copy scripts.
|
||||
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/
|
||||
|
||||
# Update pre-installed packages.
|
||||
RUN apt-get update && \
|
||||
apt-get -qq dist-upgrade && \
|
||||
/scripts/cleanup.sh
|
||||
|
||||
# Default working directory.
|
||||
WORKDIR /photoprism
|
||||
|
||||
# Expose HTTP(S) ports.
|
||||
EXPOSE 2342 2443
|
||||
|
||||
# Copy app files.
|
||||
COPY --from=build --chown=root:root --chmod=755 /opt/photoprism/ /opt/photoprism
|
||||
|
||||
# Declare container entrypoint script.
|
||||
ENTRYPOINT ["/scripts/entrypoint.sh"]
|
||||
|
||||
# Start app.
|
||||
CMD ["/opt/photoprism/bin/photoprism", "start"]
|
6275
frontend/package-lock.json
generated
6275
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "photoprism",
|
||||
"version": "1.0.0",
|
||||
"version": "1",
|
||||
"description": "AI-Powered Photos App",
|
||||
"author": "PhotoPrism UG",
|
||||
"license": "AGPL-3.0",
|
||||
|
@ -20,45 +20,45 @@
|
|||
"gettext-compile": "gettext-compile --output src/locales/translations.json src/locales/*.po"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/cli": "^7.21.5",
|
||||
"@babel/core": "^7.21.8",
|
||||
"@babel/eslint-parser": "^7.21.8",
|
||||
"@babel/cli": "^7.23.0",
|
||||
"@babel/core": "^7.23.2",
|
||||
"@babel/eslint-parser": "^7.22.15",
|
||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.20.7",
|
||||
"@babel/plugin-transform-runtime": "^7.21.4",
|
||||
"@babel/preset-env": "^7.21.5",
|
||||
"@babel/register": "^7.21.0",
|
||||
"@babel/runtime": "^7.21.5",
|
||||
"@babel/plugin-transform-runtime": "^7.23.2",
|
||||
"@babel/preset-env": "^7.23.2",
|
||||
"@babel/register": "^7.22.15",
|
||||
"@babel/runtime": "^7.23.2",
|
||||
"@lcdp/offline-plugin": "^5.1.1",
|
||||
"@vvo/tzdb": "^6.107.0",
|
||||
"axios": "^1.4.0",
|
||||
"axios-mock-adapter": "^1.21.4",
|
||||
"babel-loader": "^9.1.2",
|
||||
"@vvo/tzdb": "^6.109.0",
|
||||
"axios": "^1.6.0",
|
||||
"axios-mock-adapter": "^1.22.0",
|
||||
"babel-loader": "^9.1.3",
|
||||
"babel-plugin-istanbul": "^6.1.1",
|
||||
"browserslist": "^4.21.5",
|
||||
"chai": "^4.3.7",
|
||||
"browserslist": "^4.22.1",
|
||||
"chai": "^4.3.10",
|
||||
"cheerio": "1.0.0-rc.10",
|
||||
"chrome-finder": "^1.0.7",
|
||||
"core-js": "^3.30.2",
|
||||
"core-js": "^3.33.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"css-loader": "^6.7.3",
|
||||
"cssnano": "^5.1.14",
|
||||
"css-loader": "^6.8.1",
|
||||
"cssnano": "^6.0.1",
|
||||
"easygettext": "^2.17.0",
|
||||
"eslint": "^8.40.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-config-standard": "^17.0.0",
|
||||
"eslint": "^8.51.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-formatter-pretty": "^5.0.0",
|
||||
"eslint-plugin-html": "^7.1.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier-vue": "^4.2.0",
|
||||
"eslint-plugin-prettier-vue": "^5.0.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-vue": "^9.13.0",
|
||||
"eslint-plugin-vue": "^9.17.0",
|
||||
"eslint-webpack-plugin": "^4.0.1",
|
||||
"eventsource-polyfill": "^0.9.6",
|
||||
"file-loader": "^6.2.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"hls.js": "^1.2.9",
|
||||
"hls.js": "^1.4.12",
|
||||
"i": "^0.3.7",
|
||||
"karma": "^6.4.2",
|
||||
"karma-chrome-launcher": "^3.2.0",
|
||||
|
@ -67,31 +67,31 @@
|
|||
"karma-mocha": "^2.0.1",
|
||||
"karma-verbose-reporter": "^0.0.8",
|
||||
"karma-webpack": "^5.0.0",
|
||||
"luxon": "^3.3.0",
|
||||
"maplibre-gl": "^2.4.0",
|
||||
"luxon": "^3.4.3",
|
||||
"maplibre-gl": "^3.5.1",
|
||||
"memoize-one": "^6.0.0",
|
||||
"mini-css-extract-plugin": "^2.7.2",
|
||||
"mini-css-extract-plugin": "^2.7.6",
|
||||
"minimist": ">=1.2.5",
|
||||
"mocha": "^10.2.0",
|
||||
"node-storage-shim": "^2.0.1",
|
||||
"photoswipe": "^4.1.3",
|
||||
"postcss": "^8.4.20",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-loader": "^7.0.2",
|
||||
"postcss-preset-env": "^7.8.3",
|
||||
"postcss-loader": "^7.3.3",
|
||||
"postcss-preset-env": "^9.2.0",
|
||||
"postcss-reporter": "^7.0.5",
|
||||
"postcss-url": "^10.1.3",
|
||||
"prettier": "^2.8.8",
|
||||
"prettier": "^3.0.3",
|
||||
"pubsub-js": "^1.9.4",
|
||||
"regenerator-runtime": "^0.13.11",
|
||||
"regenerator-runtime": "^0.14.0",
|
||||
"resolve-url-loader": "^5.0.0",
|
||||
"sass": "^1.62.1",
|
||||
"sass-loader": "^13.2.0",
|
||||
"sass": "^1.69.4",
|
||||
"sass-loader": "^13.3.2",
|
||||
"server": "^1.0.38",
|
||||
"sockette": "^2.0.6",
|
||||
"style-loader": "^3.3.1",
|
||||
"style-loader": "^3.3.3",
|
||||
"svg-url-loader": "^8.0.0",
|
||||
"tar": "^6.1.13",
|
||||
"tar": "^6.2.0",
|
||||
"url-loader": "^4.1.1",
|
||||
"util": "^0.12.5",
|
||||
"vue": "^2.7.14",
|
||||
|
@ -106,17 +106,17 @@
|
|||
"vue-template-compiler": "^2.7.13",
|
||||
"vue2-filters": "^0.14.0",
|
||||
"vuetify": "^1.5.24",
|
||||
"webpack": "^5.83.0",
|
||||
"webpack-bundle-analyzer": "^4.8.0",
|
||||
"webpack-cli": "^5.1.1",
|
||||
"webpack-hot-middleware": "^2.25.3",
|
||||
"webpack": "^5.89.0",
|
||||
"webpack-bundle-analyzer": "^4.9.1",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-hot-middleware": "^2.25.4",
|
||||
"webpack-manifest-plugin": "^5.0.0",
|
||||
"webpack-md5-hash": "^0.0.6",
|
||||
"webpack-merge": "^5.8.0"
|
||||
"webpack-merge": "^5.10.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16.0.0",
|
||||
"npm": ">= 8.0.0",
|
||||
"node": ">= 18.0.0",
|
||||
"npm": ">= 9.0.0",
|
||||
"yarn": "please use npm"
|
||||
},
|
||||
"browserslist": ">0.25% and last 2 years"
|
||||
|
|
|
@ -164,9 +164,9 @@ export default [
|
|||
},
|
||||
{
|
||||
name: "moment",
|
||||
path: "/moments/:uid/:slug",
|
||||
path: "/moments/:album/:slug",
|
||||
component: AlbumPhotos,
|
||||
meta: { collName: "Moments", collRoute: "moments", auth: true },
|
||||
meta: { collectionTitle: "Moments", collectionRoute: "moments", auth: true },
|
||||
},
|
||||
{
|
||||
name: "albums",
|
||||
|
@ -177,9 +177,9 @@ export default [
|
|||
},
|
||||
{
|
||||
name: "album",
|
||||
path: "/albums/:uid/:slug",
|
||||
path: "/albums/:album/:slug",
|
||||
component: AlbumPhotos,
|
||||
meta: { collName: "Albums", collRoute: "albums", auth: true },
|
||||
meta: { collectionTitle: "Albums", collectionRoute: "albums", auth: true },
|
||||
},
|
||||
{
|
||||
name: "calendar",
|
||||
|
@ -190,9 +190,9 @@ export default [
|
|||
},
|
||||
{
|
||||
name: "month",
|
||||
path: "/calendar/:uid/:slug",
|
||||
path: "/calendar/:album/:slug",
|
||||
component: AlbumPhotos,
|
||||
meta: { collName: "Calendar", collRoute: "calendar", auth: true },
|
||||
meta: { collectionTitle: "Calendar", collectionRoute: "calendar", auth: true },
|
||||
},
|
||||
{
|
||||
name: "folders",
|
||||
|
@ -203,9 +203,9 @@ export default [
|
|||
},
|
||||
{
|
||||
name: "folder",
|
||||
path: "/folders/:uid/:slug",
|
||||
path: "/folders/:album/:slug",
|
||||
component: AlbumPhotos,
|
||||
meta: { collName: "Folders", collRoute: "folders", auth: true },
|
||||
meta: { collectionTitle: "Folders", collectionRoute: "folders", auth: true },
|
||||
},
|
||||
{
|
||||
name: "unsorted",
|
||||
|
@ -263,16 +263,25 @@ export default [
|
|||
meta: { title: $gettext("Places"), auth: true },
|
||||
},
|
||||
{
|
||||
name: "places_query",
|
||||
path: "/places/:q",
|
||||
name: "places_view",
|
||||
path: "/places/view/:s",
|
||||
component: Places,
|
||||
meta: { title: $gettext("Places"), auth: true },
|
||||
},
|
||||
{
|
||||
name: "places_scope",
|
||||
path: "/places/:s/:q",
|
||||
component: Places,
|
||||
name: "places_browse",
|
||||
path: "/places/browse",
|
||||
component: Photos,
|
||||
meta: { title: $gettext("Places"), auth: true },
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (session.loginRequired()) {
|
||||
next({ name: "login" });
|
||||
} else if (config.deny("photos", "search")) {
|
||||
next({ name: "albums" });
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "states",
|
||||
|
@ -283,9 +292,9 @@ export default [
|
|||
},
|
||||
{
|
||||
name: "state",
|
||||
path: "/states/:uid/:slug",
|
||||
path: "/states/:album/:slug",
|
||||
component: AlbumPhotos,
|
||||
meta: { collName: "Places", collRoute: "states", auth: true },
|
||||
meta: { collectionTitle: "Places", collectionRoute: "states", auth: true },
|
||||
},
|
||||
{
|
||||
name: "files",
|
||||
|
|
|
@ -327,6 +327,12 @@ export default class Config {
|
|||
case "files":
|
||||
this.values.count.files += data.count;
|
||||
break;
|
||||
case "hidden":
|
||||
this.values.count.hidden += data.count;
|
||||
break;
|
||||
case "archived":
|
||||
this.values.count.archived += data.count;
|
||||
break;
|
||||
case "favorites":
|
||||
this.values.count.favorites += data.count;
|
||||
break;
|
||||
|
|
|
@ -84,11 +84,14 @@ export default class Session {
|
|||
this.config.progress(80);
|
||||
this.redeemToken(shared.token).finally(() => {
|
||||
this.config.progress(99);
|
||||
if (shared.uri) {
|
||||
window.location = shared.uri;
|
||||
} else {
|
||||
window.location = this.config.baseUri + "/";
|
||||
}
|
||||
|
||||
// Redirect URL.
|
||||
const location = shared.uri ? shared.uri : this.config.baseUri + "/";
|
||||
|
||||
// Redirect to URL after one second.
|
||||
setTimeout(() => {
|
||||
window.location = location;
|
||||
}, 1000);
|
||||
});
|
||||
} else {
|
||||
this.config.progress(80);
|
||||
|
@ -100,7 +103,7 @@ export default class Session {
|
|||
}
|
||||
|
||||
useSessionStorage() {
|
||||
this.deleteId();
|
||||
this.reset();
|
||||
this.storage.setItem(this.storage_key, "true");
|
||||
this.storage = window.sessionStorage;
|
||||
}
|
||||
|
@ -112,7 +115,7 @@ export default class Session {
|
|||
|
||||
applyId(id) {
|
||||
if (!id) {
|
||||
this.deleteId();
|
||||
this.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -146,8 +149,6 @@ export default class Session {
|
|||
this.storage.removeItem("session_id");
|
||||
|
||||
delete Api.defaults.headers.common[SessionHeader];
|
||||
|
||||
this.deleteAll();
|
||||
}
|
||||
|
||||
setResp(resp) {
|
||||
|
@ -272,9 +273,17 @@ export default class Session {
|
|||
this.storage.removeItem("user");
|
||||
}
|
||||
|
||||
deleteAll() {
|
||||
deleteClipboard() {
|
||||
this.storage.removeItem("clipboard");
|
||||
this.storage.removeItem("photo_clipboard");
|
||||
this.storage.removeItem("album_clipboard");
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.deleteId();
|
||||
this.deleteData();
|
||||
this.deleteUser();
|
||||
this.deleteClipboard();
|
||||
}
|
||||
|
||||
sendClientInfo() {
|
||||
|
@ -303,16 +312,20 @@ export default class Session {
|
|||
}
|
||||
|
||||
login(username, password, token) {
|
||||
this.deleteId();
|
||||
this.reset();
|
||||
|
||||
return Api.post("session", { username, password, token }).then((resp) => {
|
||||
const reload = this.config.getLanguage() !== resp.data?.config?.settings?.ui?.language;
|
||||
this.setResp(resp);
|
||||
this.sendClientInfo();
|
||||
this.onLogin();
|
||||
return Promise.resolve(reload);
|
||||
});
|
||||
}
|
||||
|
||||
onLogin() {
|
||||
this.sendClientInfo();
|
||||
}
|
||||
|
||||
refresh() {
|
||||
// Refresh session information.
|
||||
if (this.config.isPublic()) {
|
||||
|
@ -330,7 +343,7 @@ export default class Session {
|
|||
return Promise.resolve();
|
||||
})
|
||||
.catch(() => {
|
||||
this.deleteId();
|
||||
this.reset();
|
||||
if (!this.isLogin()) {
|
||||
window.location.reload();
|
||||
}
|
||||
|
@ -354,7 +367,7 @@ export default class Session {
|
|||
}
|
||||
|
||||
onLogout(noRedirect) {
|
||||
this.deleteId();
|
||||
this.reset();
|
||||
if (noRedirect !== true && !this.isLogin()) {
|
||||
window.location = this.config.baseUri + "/";
|
||||
}
|
||||
|
|
|
@ -153,6 +153,14 @@ export default class Util {
|
|||
return s.replace(/\w\S*/g, (w) => w.replace(/^\w/, (c) => c.toUpperCase()));
|
||||
}
|
||||
|
||||
static ucFirst(s) {
|
||||
if (!s || s === "") {
|
||||
return "";
|
||||
}
|
||||
|
||||
return s.charAt(0).toUpperCase() + s.slice(1);
|
||||
}
|
||||
|
||||
static generateToken() {
|
||||
return (Math.random() + 1).toString(36).substring(6);
|
||||
}
|
||||
|
@ -171,6 +179,7 @@ export default class Util {
|
|||
return "Unprocessed Sensor Data (RAW)";
|
||||
case "mov":
|
||||
case "qt":
|
||||
case "qt ":
|
||||
return "Apple QuickTime";
|
||||
case "bmp":
|
||||
return "Bitmap";
|
||||
|
@ -193,8 +202,9 @@ export default class Util {
|
|||
return "AOMedia Video 1 (AV1)";
|
||||
case "avifs":
|
||||
return "AVIF Image Sequence";
|
||||
case "hevc":
|
||||
case "hvc":
|
||||
case "hevc":
|
||||
case "hev1":
|
||||
case "hvc1":
|
||||
return "High Efficiency Video Coding (HEVC) / H.265";
|
||||
case "m4v":
|
||||
|
@ -235,6 +245,31 @@ export default class Util {
|
|||
}
|
||||
}
|
||||
|
||||
static formatCodec(codec) {
|
||||
if (!codec) {
|
||||
return "";
|
||||
}
|
||||
|
||||
switch (codec) {
|
||||
case "webp":
|
||||
case "extended webp":
|
||||
return "WebP";
|
||||
case "webm":
|
||||
return "WebM";
|
||||
case "av1c":
|
||||
case "av01":
|
||||
return "AV1";
|
||||
case "avc1":
|
||||
return "AVC";
|
||||
case "hvc":
|
||||
case "hev1":
|
||||
case "hvc1":
|
||||
return "HEVC";
|
||||
default:
|
||||
return codec.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
static codecName(value) {
|
||||
if (!value || typeof value !== "string") {
|
||||
return "";
|
||||
|
@ -245,16 +280,19 @@ export default class Util {
|
|||
return "Unprocessed Sensor Data (RAW)";
|
||||
case "mov":
|
||||
case "qt":
|
||||
case "qt ":
|
||||
return "Apple QuickTime (MOV)";
|
||||
case "avc":
|
||||
case "avc1":
|
||||
return "Advanced Video Coding (AVC) / H.264";
|
||||
case "hevc":
|
||||
case "hvc":
|
||||
case "hevc":
|
||||
case "hev1":
|
||||
case "hvc1":
|
||||
return "High Efficiency Video Coding (HEVC) / H.265";
|
||||
case "vvc":
|
||||
return "Versatile Video Coding (VVC) / H.266";
|
||||
case "av1c":
|
||||
case "av01":
|
||||
return "AOMedia Video 1 (AV1)";
|
||||
case "gif":
|
||||
|
@ -263,6 +301,8 @@ export default class Util {
|
|||
return "Matroska Multimedia Container (MKV)";
|
||||
case "webp":
|
||||
return "Google WebP";
|
||||
case "extended webp":
|
||||
return "Extended WebP";
|
||||
case "webm":
|
||||
return "Google WebM";
|
||||
case "mpeg":
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
<v-toolbar flat :dense="$vuetify.breakpoint.smAndDown" class="page-toolbar" color="secondary">
|
||||
<v-toolbar-title :title="album.Title">
|
||||
<span class="hidden-xs-only">
|
||||
<router-link :to="{ name: collRoute }">
|
||||
{{ T(collName) }}
|
||||
<router-link :to="{ name: collectionRoute }">
|
||||
{{ T(collectionTitle) }}
|
||||
</router-link>
|
||||
<v-icon>{{ navIcon }}</v-icon>
|
||||
</span>
|
||||
|
@ -33,10 +33,12 @@
|
|||
<v-icon>get_app</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="settings.view === 'cards'" icon class="action-view-list" :title="$gettext('Toggle View')" @click.stop="setView('list')">
|
||||
<v-btn v-if="settings.view === 'cards'" icon class="action-view-list" :title="$gettext('Toggle View')"
|
||||
@click.stop="setView('list')">
|
||||
<v-icon>view_list</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="settings.view === 'list'" icon class="action-view-mosaic" :title="$gettext('Toggle View')" @click.stop="setView('mosaic')">
|
||||
<v-btn v-else-if="settings.view === 'list'" icon class="action-view-mosaic" :title="$gettext('Toggle View')"
|
||||
@click.stop="setView('mosaic')">
|
||||
<v-icon>view_comfy</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else icon class="action-view-cards" :title="$gettext('Toggle View')" @click.stop="setView('cards')">
|
||||
|
@ -68,7 +70,8 @@
|
|||
|
||||
<p-share-dialog :show="dialog.share" :model="album" @upload="webdavUpload"
|
||||
@close="dialog.share = false"></p-share-dialog>
|
||||
<p-share-upload-dialog :show="dialog.upload" :items="{albums: album.getId()}" :model="album" @cancel="dialog.upload = false"
|
||||
<p-share-upload-dialog :show="dialog.upload" :items="{albums: album.getId()}" :model="album"
|
||||
@cancel="dialog.upload = false"
|
||||
@confirm="dialog.upload = false"></p-share-upload-dialog>
|
||||
<p-album-edit-dialog :show="dialog.edit" :album="album" @close="dialog.edit = false"></p-album-edit-dialog>
|
||||
</v-form>
|
||||
|
@ -77,34 +80,40 @@
|
|||
import Event from "pubsub-js";
|
||||
import Notify from "common/notify";
|
||||
import download from "common/download";
|
||||
import { T } from "common/vm";
|
||||
import {T} from "common/vm";
|
||||
|
||||
export default {
|
||||
name: 'PAlbumToolbar',
|
||||
props: {
|
||||
album: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
default: () => {
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
default: () => {
|
||||
},
|
||||
},
|
||||
updateFilter: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
default: () => {
|
||||
},
|
||||
},
|
||||
updateQuery: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
default: () => {
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
default: () => {
|
||||
},
|
||||
},
|
||||
refresh: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
default: () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
|
@ -125,8 +134,8 @@ export default {
|
|||
experimental: this.$config.get("experimental"),
|
||||
isFullScreen: !!document.fullscreenElement,
|
||||
categories: this.$config.albumCategories(),
|
||||
collName: this.$route.meta && this.$route.meta.collName ? this.$route.meta.collName : this.$gettext("Albums"),
|
||||
collRoute: this.$route.meta && this.$route.meta.collRoute ? this.$route.meta.collRoute : "albums",
|
||||
collectionTitle: this.$route.meta?.collectionTitle ? this.$route.meta.collectionTitle : this.$gettext("Albums"),
|
||||
collectionRoute: this.$route.meta?.collectionRoute ? this.$route.meta.collectionRoute : "albums",
|
||||
navIcon: this.$rtl ? 'navigate_before' : 'navigate_next',
|
||||
searchExpanded: false,
|
||||
options: {
|
||||
|
@ -167,7 +176,12 @@ export default {
|
|||
this.dialog.upload = true;
|
||||
},
|
||||
showUpload() {
|
||||
Event.publish("dialog.upload");
|
||||
// Pre-select manually managed albums in upload dialog.
|
||||
if(this.album.Type === "album") {
|
||||
Event.publish("dialog.upload", {albums: [this.album]});
|
||||
} else {
|
||||
Event.publish("dialog.upload", {albums: []});
|
||||
}
|
||||
},
|
||||
expand() {
|
||||
this.searchExpanded = !this.searchExpanded;
|
||||
|
|
|
@ -1,28 +1,23 @@
|
|||
<template>
|
||||
<div class="auth-footer">
|
||||
<footer v-if="sponsor">
|
||||
<footer>
|
||||
<v-layout wrap align-top pa-0 ma-0>
|
||||
<v-flex xs12 sm6 class="pa-0 body-2 text-selectable text-xs-center white--text text-sm-left">
|
||||
{{ $config.getAbout() }}
|
||||
{{ about }}
|
||||
</v-flex>
|
||||
|
||||
<v-flex v-if="config.legalInfo" xs12 sm6 class="pa-0 body-2 text-xs-center text-sm-right white--text">
|
||||
<a v-if="config.legalUrl" :href="config.legalUrl" target="_blank" class="text-link"
|
||||
:style="`color: ${colors.link}!important`">{{ config.legalInfo }}</a>
|
||||
<span v-else>{{ config.legalInfo }}</span>
|
||||
<v-flex v-if="legalInfo" xs12 sm6 class="pa-0 body-2 text-xs-center text-sm-right white--text">
|
||||
<a v-if="legalUrl" :href="legalUrl" target="_blank" class="text-link"
|
||||
:style="`color: ${colors.link}!important`">{{ legalInfo }}</a>
|
||||
<span v-else>{{ legalInfo }}</span>
|
||||
</v-flex>
|
||||
<v-flex v-else xs12 class="pa-0 body-2 text-selectable text-xs-center white--text text-sm-right sm6">
|
||||
<strong>{{ config.siteCaption ? config.siteCaption : config.siteTitle }}</strong>
|
||||
<v-flex v-else-if="caption" xs12 sm6
|
||||
class="pa-0 body-2 text-selectable text-xs-center text-sm-right white--text">
|
||||
<strong>{{ caption }}</strong>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</footer>
|
||||
<footer v-else>
|
||||
<v-layout wrap align-top pa-0 ma-0>
|
||||
<v-flex xs12 sm6 class="pa-0 body-2 text-xs-center text-sm-left white--text text-selectable">
|
||||
<strong>{{ config.siteTitle }}</strong> – {{ config.siteCaption }}
|
||||
</v-flex>
|
||||
<v-flex xs12 sm6 class="pa-0 body-2 text-selectable text-xs-center text-sm-right">
|
||||
<router-link to="/about" class="text-link"><span class="white--text">Made with ❤️ in Berlin</span></router-link>
|
||||
<v-flex v-else xs12 sm6 class="pa-0 body-2 text-selectable text-xs-center text-sm-right white--text">
|
||||
<router-link to="/about" class="text-link"><span class="white--text">Made with ❤️ in Berlin</span>
|
||||
</router-link>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</footer>
|
||||
|
@ -46,9 +41,14 @@ export default {
|
|||
},
|
||||
},
|
||||
data() {
|
||||
const config = this.$config;
|
||||
return {
|
||||
sponsor: this.$config.isSponsor(),
|
||||
config: this.$config.values,
|
||||
about: config.getAbout(),
|
||||
sponsor: config.isSponsor(),
|
||||
caption: config.values.siteCaption ? config.values.siteCaption : config.values.siteTitle,
|
||||
legalUrl: config.values.legalUrl,
|
||||
legalInfo: config.values.legalInfo,
|
||||
config: config.values,
|
||||
rtl: this.$rtl,
|
||||
};
|
||||
},
|
||||
|
|
|
@ -33,6 +33,7 @@ import PPhotoToolbar from "component/photo/toolbar.vue";
|
|||
import PPhotoCards from "component/photo/cards.vue";
|
||||
import PPhotoMosaic from "component/photo/mosaic.vue";
|
||||
import PPhotoList from "component/photo/list.vue";
|
||||
import PPhotoPreview from "component/photo/preview.vue";
|
||||
import PPhotoClipboard from "component/photo/clipboard.vue";
|
||||
import PAlbumClipboard from "component/album/clipboard.vue";
|
||||
import PAlbumToolbar from "component/album/toolbar.vue";
|
||||
|
@ -59,6 +60,7 @@ components.install = (Vue) => {
|
|||
Vue.component("PPhotoCards", PPhotoCards);
|
||||
Vue.component("PPhotoMosaic", PPhotoMosaic);
|
||||
Vue.component("PPhotoList", PPhotoList);
|
||||
Vue.component("PPhotoPreview", PPhotoPreview);
|
||||
Vue.component("PPhotoClipboard", PPhotoClipboard);
|
||||
Vue.component("PAlbumClipboard", PAlbumClipboard);
|
||||
Vue.component("PAlbumToolbar", PAlbumToolbar);
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
</v-list>
|
||||
</v-toolbar>
|
||||
|
||||
<v-list class="pt-3 p-flex-menu">
|
||||
<v-list class="pt-3 p-flex-menu navigation-menu">
|
||||
<v-list-tile v-if="isMini && !isRestricted" class="nav-expand" @click.stop="toggleIsMini()">
|
||||
<v-list-tile-action :title="$gettext('Expand')">
|
||||
<v-icon v-if="!rtl">chevron_right</v-icon>
|
||||
|
@ -165,6 +165,8 @@
|
|||
<v-list-tile-content>
|
||||
<v-list-tile-title :class="`p-flex-menuitem menu-item ${rtl ? '--rtl' : ''}`">
|
||||
<translate>Archive</translate>
|
||||
<span v-show="config.count.archived > 0"
|
||||
:class="`nav-count ${rtl ? '--rtl' : ''}`">{{ config.count.archived | abbreviateCount }}</span>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
@ -522,6 +524,18 @@
|
|||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile v-show="featMembership" :to="{ name: 'upgrade' }" class="nav-membership" @click.stop="">
|
||||
<v-list-tile-action :title="$gettext('Support Our Mission')">
|
||||
<v-icon>diamond</v-icon>
|
||||
</v-list-tile-action>
|
||||
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate key="Support Our Mission">Support Our Mission</translate>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
</v-list>
|
||||
|
||||
<v-list class="p-user-box">
|
||||
|
@ -539,7 +553,7 @@
|
|||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile v-show="auth && !isPublic && $config.feature('account')" class="p-profile" @click.stop="onAccount">
|
||||
<v-list-tile v-show="auth && !isPublic" class="p-profile" @click.stop="onAccountSettings">
|
||||
<v-list-tile-avatar size="36">
|
||||
<img :src="userAvatarURL" :alt="accountInfo" :title="accountInfo">
|
||||
</v-list-tile-avatar>
|
||||
|
@ -664,7 +678,7 @@
|
|||
<span v-else>{{ config.legalInfo }}</span>
|
||||
</div>
|
||||
<p-reload-dialog :show="reload.dialog" @close="reload.dialog = false"></p-reload-dialog>
|
||||
<p-upload-dialog :show="upload.dialog" @cancel="upload.dialog = false"
|
||||
<p-upload-dialog :show="upload.dialog" :data="upload.data" @cancel="upload.dialog = false"
|
||||
@confirm="upload.dialog = false"></p-upload-dialog>
|
||||
<p-photo-edit-dialog :show="edit.dialog" :selection="edit.selection" :index="edit.index" :album="edit.album"
|
||||
@close="edit.dialog = false"></p-photo-edit-dialog>
|
||||
|
@ -673,6 +687,7 @@
|
|||
|
||||
<script>
|
||||
import Event from "pubsub-js";
|
||||
import Album from "model/album";
|
||||
|
||||
export default {
|
||||
name: "PNavigation",
|
||||
|
@ -702,6 +717,7 @@ export default {
|
|||
const isReadOnly = this.$config.get("readonly");
|
||||
const isRestricted = this.$config.deny("photos", "access_library");
|
||||
const isSuperAdmin = this.$session.isSuperAdmin();
|
||||
const tier = this.$config.getTier();
|
||||
|
||||
return {
|
||||
canSearchPlaces: this.$config.allow("places", "search"),
|
||||
|
@ -715,7 +731,8 @@ export default {
|
|||
appIcon: this.$config.getIcon(),
|
||||
indexing: false,
|
||||
drawer: null,
|
||||
featUpgrade: this.$config.getTier() < 8 && isSuperAdmin && !isPublic && !isDemo,
|
||||
featUpgrade: tier < 6 && isSuperAdmin && !isPublic && !isDemo,
|
||||
featMembership: tier < 3 && isSuperAdmin && !isPublic && !isDemo,
|
||||
isRestricted: isRestricted,
|
||||
isMini: localStorage.getItem('last_navigation_mode') !== 'false' || isRestricted,
|
||||
isDemo: isDemo,
|
||||
|
@ -734,6 +751,7 @@ export default {
|
|||
},
|
||||
upload: {
|
||||
dialog: false,
|
||||
data: {},
|
||||
},
|
||||
edit: {
|
||||
dialog: false,
|
||||
|
@ -777,7 +795,14 @@ export default {
|
|||
this.subscriptions.push(Event.subscribe('index', this.onIndex));
|
||||
this.subscriptions.push(Event.subscribe('import', this.onIndex));
|
||||
this.subscriptions.push(Event.subscribe("dialog.reload", () => this.reload.dialog = true));
|
||||
this.subscriptions.push(Event.subscribe("dialog.upload", () => this.upload.dialog = true));
|
||||
this.subscriptions.push(Event.subscribe("dialog.upload", (ev, data) => {
|
||||
if(data) {
|
||||
this.upload.data = data;
|
||||
} else {
|
||||
this.upload.data = {};
|
||||
}
|
||||
this.upload.dialog = true;
|
||||
}));
|
||||
this.subscriptions.push(Event.subscribe("dialog.edit", (ev, data) => {
|
||||
if (!this.edit.dialog) {
|
||||
this.edit.dialog = true;
|
||||
|
@ -807,7 +832,18 @@ export default {
|
|||
},
|
||||
openUpload() {
|
||||
if (this.auth && !this.isReadOnly && this.$config.feature('upload')) {
|
||||
this.upload.dialog = true;
|
||||
if (this.$route.name === 'album' && this.$route.params?.album) {
|
||||
return new Album().find(this.$route.params?.album).then(m => {
|
||||
this.upload.dialog = true;
|
||||
this.upload.data = {albums: [m]};
|
||||
}).catch(() => {
|
||||
this.upload.dialog = true;
|
||||
this.upload.data = {albums: []};
|
||||
});
|
||||
} else {
|
||||
this.upload.dialog = true;
|
||||
this.upload.data = {albums: []};
|
||||
}
|
||||
} else {
|
||||
this.goHome();
|
||||
}
|
||||
|
@ -827,8 +863,12 @@ export default {
|
|||
this.isMini = !this.isMini;
|
||||
localStorage.setItem('last_navigation_mode', `${this.isMini}`);
|
||||
},
|
||||
onAccount: function () {
|
||||
this.$router.push({name: "settings_account"});
|
||||
onAccountSettings: function () {
|
||||
if (this.$config.feature('account')) {
|
||||
this.$router.push({name: "settings_account"});
|
||||
} else {
|
||||
this.$router.push({name: "settings"});
|
||||
}
|
||||
},
|
||||
onInfo() {
|
||||
if (this.isSponsor && this.config.legalUrl) {
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
ref="items"
|
||||
:key="photo.ID"
|
||||
:data-index="index"
|
||||
class="flex xs12 sm6 md4 lg3 xlg2 xxxl1 d-flex"
|
||||
class="flex xs12 sm6 md4 lg3 xlg2 ul1 d-flex"
|
||||
>
|
||||
<div v-if="index < firstVisibleElementIndex || index > lastVisibileElementIndex"
|
||||
:data-uid="photo.UID"
|
||||
|
@ -92,7 +92,7 @@
|
|||
>
|
||||
<v-layout v-if="photo.Type === 'live' || photo.Type === 'animated'" class="live-player">
|
||||
<video :id="'live-player-' + photo.ID" :key="photo.ID" width="500" height="500" preload="none"
|
||||
loop muted playsinline>
|
||||
loop muted playsinline>
|
||||
<source :src="photo.videoUrl()">
|
||||
</video>
|
||||
</v-layout>
|
||||
|
@ -203,6 +203,11 @@
|
|||
<i>movie</i>
|
||||
{{ photo.getVideoInfo() }}
|
||||
</button>
|
||||
<button v-else-if="photo.Type === 'live'" :title="$gettext('Live')"
|
||||
@click.exact="openPhoto(index)">
|
||||
<i>play_circle</i>
|
||||
{{ photo.getVideoInfo() }}
|
||||
</button>
|
||||
<button v-else-if="photo.Type === 'animated'" :title="$gettext('Animated')+' GIF'"
|
||||
@click.exact="openPhoto(index)">
|
||||
<i>gif_box</i>
|
||||
|
@ -218,6 +223,11 @@
|
|||
<i>photo_camera</i>
|
||||
{{ photo.getPhotoInfo() }}
|
||||
</button>
|
||||
<button v-if="photo.LensID > 1 || photo.FocalLength" :title="$gettext('Lens')" class="action-lens-edit"
|
||||
:data-uid="photo.UID" @click.exact="editPhoto(index)">
|
||||
<i>camera</i>
|
||||
{{ photo.getLensInfo() }}
|
||||
</button>
|
||||
<template v-if="filter.order === 'name' && $config.feature('download')">
|
||||
<br>
|
||||
<button :title="$gettext('Name')"
|
||||
|
@ -457,7 +467,7 @@ export default {
|
|||
/**
|
||||
* updating the clipboard does not rerender this component. Because of that
|
||||
* there can be scenarios where the select-icon is missing after a change,
|
||||
* for example when selecting mutliple elements at once. We therefore
|
||||
* for example when selecting multiple elements at once. We therefore
|
||||
* force an update to fix that.
|
||||
*/
|
||||
this.$forceUpdate();
|
||||
|
|
|
@ -163,6 +163,10 @@ import Photo from "model/photo";
|
|||
export default {
|
||||
name: 'PPhotoClipboard',
|
||||
props: {
|
||||
context: {
|
||||
type: String,
|
||||
default: 'photos',
|
||||
},
|
||||
selection: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
|
@ -175,10 +179,6 @@ export default {
|
|||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
context: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
const features = this.$config.settings().features;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<v-container grid-list-xs fluid class="pa-2 p-photos p-photo-mosaic">
|
||||
<template v-if="photos.length === 0">
|
||||
<div v-if="photos.length === 0" class="pa-0">
|
||||
<v-alert
|
||||
:value="true"
|
||||
color="secondary-dark"
|
||||
|
@ -24,7 +24,7 @@
|
|||
</template>
|
||||
</p>
|
||||
</v-alert>
|
||||
</template>
|
||||
</div>
|
||||
<v-layout row wrap class="search-results photo-results mosaic-view" :class="{'select-results': selectMode}">
|
||||
<div
|
||||
v-for="(photo, index) in photos"
|
||||
|
@ -60,7 +60,7 @@
|
|||
@mouseleave="pauseLive(photo)">
|
||||
<v-layout v-if="photo.Type === 'live' || photo.Type === 'animated'" class="live-player">
|
||||
<video :id="'live-player-' + photo.ID" :key="photo.ID" width="224" height="224" preload="none"
|
||||
loop muted playsinline>
|
||||
loop muted playsinline>
|
||||
<source :src="photo.videoUrl()">
|
||||
</video>
|
||||
</v-layout>
|
||||
|
|
49
frontend/src/component/photo/preview.vue
Normal file
49
frontend/src/component/photo/preview.vue
Normal file
|
@ -0,0 +1,49 @@
|
|||
<template>
|
||||
<div class="p-photo-preview pa-0 ma-0 elevation-0 v-card v-sheet v-sheet--tile no-transition" :title="title">
|
||||
<div class="v-responsive v-image card darken-1 elevation-0 clickable" @click.prevent.stop="openPhoto">
|
||||
<div class="v-responsive__sizer" style="padding-bottom: 100%;"></div>
|
||||
<div class="v-image__image v-image__image--cover" :style="cover"></div>
|
||||
<div class="v-responsive__content"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Photo from "model/photo";
|
||||
import Thumb from "model/thumb";
|
||||
|
||||
export default {
|
||||
name: 'PPhotoPreview',
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
default: () => new Photo(false),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
url: this.model.thumbnailUrl('tile_500'),
|
||||
title: this.model.Title ? this.model.Title : '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
cover() {
|
||||
return `background-image: url('${this.url}'); background-position: center center;`;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
model() {
|
||||
this.url = this.model.thumbnailUrl('tile_500');
|
||||
this.title = this.model.Title ? this.model.Title : '';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openPhoto() {
|
||||
if (!this.$viewer || !this.model) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$viewer.show(Thumb.fromFiles([this.model]), 0);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -1,46 +1,68 @@
|
|||
<template>
|
||||
<v-form ref="form" lazy-validation
|
||||
dense autocomplete="off" class="p-photo-toolbar" accept-charset="UTF-8"
|
||||
:class="{'embedded': embedded}"
|
||||
@submit.prevent="updateQuery()">
|
||||
<v-toolbar flat :dense="$vuetify.breakpoint.smAndDown" class="page-toolbar" color="secondary">
|
||||
<v-text-field :value="filter.q"
|
||||
class="input-search background-inherit elevation-0"
|
||||
solo hide-details clearable overflow single-line
|
||||
validate-on-blur
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
browser-autocomplete="off"
|
||||
:label="$gettext('Search')"
|
||||
prepend-inner-icon="search"
|
||||
color="secondary-dark"
|
||||
@change="(v) => {updateFilter({'q': v})}"
|
||||
@keyup.enter.native="(e) => updateQuery({'q': e.target.value})"
|
||||
@click:clear="() => {updateQuery({'q': ''})}"
|
||||
></v-text-field>
|
||||
<v-toolbar flat :dense="$vuetify.breakpoint.smAndDown" :height="embedded ? 45 : undefined"
|
||||
class="page-toolbar" color="secondary">
|
||||
<template v-if="!embedded">
|
||||
<v-text-field :value="filter.q"
|
||||
class="input-search background-inherit elevation-0"
|
||||
solo hide-details clearable overflow single-line validate-on-blur
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
browser-autocomplete="off"
|
||||
:label="$gettext('Search')"
|
||||
prepend-inner-icon="search"
|
||||
color="secondary-dark"
|
||||
@change="(v) => {updateFilter({'q': v})}"
|
||||
@keyup.enter.native="(e) => updateQuery({'q': e.target.value})"
|
||||
@click:clear="() => {updateQuery({'q': ''})}"
|
||||
></v-text-field>
|
||||
|
||||
<v-btn icon class="hidden-xs-only action-reload" :title="$gettext('Reload')" @click.stop="refresh()">
|
||||
<v-icon>refresh</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-if="filter.latlng" icon :title="$gettext('Show more')" class="action-clear-location" @click.stop="clearLocation()">
|
||||
<v-icon>location_off</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="settings.view === 'cards'" icon :title="$gettext('Toggle View')" @click.stop="setView('list')">
|
||||
<v-icon>view_list</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="settings.view === 'list'" icon :title="$gettext('Toggle View')" @click.stop="setView('mosaic')">
|
||||
<v-icon>view_comfy</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else icon :title="$gettext('Toggle View')" @click.stop="setView('cards')">
|
||||
<v-icon>view_column</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon class="hidden-xs-only action-reload" :title="$gettext('Reload')" @click.stop="refresh()">
|
||||
<v-icon>refresh</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="!$config.values.readonly && $config.feature('upload')" icon class="hidden-sm-and-down action-upload"
|
||||
:title="$gettext('Upload')" @click.stop="showUpload()">
|
||||
<v-icon>cloud_upload</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-if="settings.view === 'cards'" icon :title="$gettext('Toggle View')" @click.stop="setView('list')">
|
||||
<v-icon>view_list</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="settings.view === 'list'" icon :title="$gettext('Toggle View')"
|
||||
@click.stop="setView('mosaic')">
|
||||
<v-icon>view_comfy</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else icon :title="$gettext('Toggle View')" @click.stop="setView('cards')">
|
||||
<v-icon>view_column</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn icon class="p-expand-search" :title="$gettext('Expand Search')"
|
||||
@click.stop="searchExpanded = !searchExpanded">
|
||||
<v-icon>{{ searchExpanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-if="canDelete && context === 'archive' && config.count.archived > 0" icon
|
||||
class="hidden-sm-and-down action-delete-all"
|
||||
:title="$gettext('Delete All')" @click.stop="deleteAll()">
|
||||
<v-icon>delete_sweep</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="canUpload" icon class="hidden-sm-and-down action-upload"
|
||||
:title="$gettext('Upload')" @click.stop="showUpload()">
|
||||
<v-icon>cloud_upload</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn icon class="p-expand-search" :title="$gettext('Expand Search')"
|
||||
@click.stop="searchExpanded = !searchExpanded">
|
||||
<v-icon>{{ searchExpanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn v-if="canAccessLibrary" icon :title="$gettext('Browse')" class="action-browse" @click.stop="onBrowse">
|
||||
<v-icon size="20">tab</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-if="onClose !== undefined" icon :title="$gettext('Close')" class="action-close" @click.stop="onClose">
|
||||
<v-icon>close</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-toolbar>
|
||||
|
||||
<v-card v-show="searchExpanded"
|
||||
|
@ -166,41 +188,77 @@
|
|||
</v-layout>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
<p-photo-delete-dialog
|
||||
:show="dialog.delete"
|
||||
:text="$gettext('Are you sure you want to delete all archived pictures?')"
|
||||
:action="$gettext('Delete All')"
|
||||
@cancel="dialog.delete = false" @confirm="batchDelete">
|
||||
</p-photo-delete-dialog>
|
||||
</v-form>
|
||||
</template>
|
||||
<script>
|
||||
import Event from "pubsub-js";
|
||||
import * as options from "options/options";
|
||||
import Api from "common/api";
|
||||
import Notify from "common/notify";
|
||||
|
||||
export default {
|
||||
name: 'PPhotoToolbar',
|
||||
props: {
|
||||
context: {
|
||||
type: String,
|
||||
default: 'photos',
|
||||
},
|
||||
filter: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
default: () => {
|
||||
},
|
||||
},
|
||||
staticFilter: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
},
|
||||
},
|
||||
updateFilter: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
default: () => {
|
||||
},
|
||||
},
|
||||
updateQuery: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
default: () => {
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
default: () => {
|
||||
},
|
||||
},
|
||||
refresh: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
default: () => {
|
||||
},
|
||||
},
|
||||
onClose: {
|
||||
type: Function,
|
||||
default: undefined,
|
||||
},
|
||||
embedded: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
data() {
|
||||
const features = this.$config.settings().features;
|
||||
const readonly = this.$config.get("readonly");
|
||||
return {
|
||||
experimental: this.$config.get("experimental"),
|
||||
isFullScreen: !!document.fullscreenElement,
|
||||
config: this.$config.values,
|
||||
readonly: readonly,
|
||||
canUpload: !readonly && !this.embedded && this.$config.allow("files", "upload") && features.upload,
|
||||
canDelete: !readonly && !this.embedded && this.$config.allow("photos", "delete") && features.delete,
|
||||
canAccessLibrary: this.$config.allow("photos", "access_library"),
|
||||
searchExpanded: false,
|
||||
all: {
|
||||
countries: [{ID: "", Name: this.$gettext("All Countries")}],
|
||||
|
@ -229,6 +287,9 @@ export default {
|
|||
{value: 'similar', text: this.$gettext('Visual Similarity')},
|
||||
],
|
||||
},
|
||||
dialog: {
|
||||
delete: false,
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -262,7 +323,37 @@ export default {
|
|||
},
|
||||
showUpload() {
|
||||
Event.publish("dialog.upload");
|
||||
}
|
||||
},
|
||||
deleteAll() {
|
||||
if (!this.canDelete) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.dialog.delete = true;
|
||||
},
|
||||
clearLocation() {
|
||||
this.$router.push({ name: "browse" });
|
||||
},
|
||||
onBrowse() {
|
||||
const route = {name: 'places_browse', query: this.staticFilter};
|
||||
const routeUrl = this.$router.resolve(route).href;
|
||||
if (routeUrl) {
|
||||
window.open(routeUrl, '_blank');
|
||||
}
|
||||
},
|
||||
batchDelete() {
|
||||
if (!this.canDelete) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.dialog.delete = false;
|
||||
|
||||
Api.post("batch/photos/delete", {"all": true}).then(() => this.onDeleted());
|
||||
},
|
||||
onDeleted() {
|
||||
Notify.success(this.$gettext("Permanently deleted"));
|
||||
this.$clipboard.clear();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
102
frontend/src/component/places/style-control.js
Normal file
102
frontend/src/component/places/style-control.js
Normal file
|
@ -0,0 +1,102 @@
|
|||
export default class MapStyleControl {
|
||||
constructor(styles, defaultStyle, setStyle) {
|
||||
this.styles = styles || MapStyleControl.DEFAULT_STYLES;
|
||||
this.defaultStyle = defaultStyle || MapStyleControl.DEFAULT_STYLE;
|
||||
this.setStyle = setStyle;
|
||||
this.onDocumentClick = this.onDocumentClick.bind(this);
|
||||
}
|
||||
|
||||
getDefaultPosition() {
|
||||
return "top-right";
|
||||
}
|
||||
|
||||
onAdd(map) {
|
||||
this.map = map;
|
||||
this.controlContainer = document.createElement("div");
|
||||
this.controlContainer.classList.add("maplibregl-ctrl");
|
||||
this.controlContainer.classList.add("maplibregl-ctrl-group");
|
||||
this.mapStyleContainer = document.createElement("div");
|
||||
this.styleButton = document.createElement("button");
|
||||
this.styleButton.type = "button";
|
||||
this.mapStyleContainer.classList.add("maplibregl-style-list");
|
||||
for (const style of this.styles) {
|
||||
const styleElement = document.createElement("button");
|
||||
styleElement.type = "button";
|
||||
styleElement.innerText = style.title;
|
||||
styleElement.classList.add(style.style, "_");
|
||||
styleElement.dataset.style = JSON.stringify(style.style);
|
||||
styleElement.addEventListener("click", (event) => {
|
||||
const srcElement = event.srcElement;
|
||||
if (srcElement.classList.contains("active")) {
|
||||
this.mapStyleContainer.style.display = "none";
|
||||
this.styleButton.style.display = "block";
|
||||
return;
|
||||
}
|
||||
|
||||
// Set new map style.
|
||||
if (typeof this.setStyle === "function") {
|
||||
this.setStyle(JSON.parse(srcElement.dataset.style));
|
||||
}
|
||||
|
||||
/*
|
||||
this.mapStyleContainer.style.display = "none";
|
||||
this.styleButton.style.display = "block";
|
||||
const elms = this.mapStyleContainer.getElementsByClassName("active");
|
||||
while (elms[0]) {
|
||||
elms[0].classList.remove("active");
|
||||
}
|
||||
srcElement.classList.add("active");
|
||||
*/
|
||||
});
|
||||
if (style.style === this.defaultStyle) {
|
||||
styleElement.classList.add("active");
|
||||
}
|
||||
this.mapStyleContainer.appendChild(styleElement);
|
||||
}
|
||||
this.styleButton.classList.add("maplibregl-ctrl-icon");
|
||||
this.styleButton.classList.add("maplibregl-style-switcher");
|
||||
this.styleButton.addEventListener("click", () => {
|
||||
this.styleButton.style.display = "none";
|
||||
this.mapStyleContainer.style.display = "block";
|
||||
});
|
||||
document.addEventListener("click", this.onDocumentClick);
|
||||
this.controlContainer.appendChild(this.styleButton);
|
||||
this.controlContainer.appendChild(this.mapStyleContainer);
|
||||
return this.controlContainer;
|
||||
}
|
||||
|
||||
onRemove() {
|
||||
if (
|
||||
!this.controlContainer ||
|
||||
!this.controlContainer.parentNode ||
|
||||
!this.map ||
|
||||
!this.styleButton
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.styleButton.removeEventListener("click", this.onDocumentClick);
|
||||
this.controlContainer.parentNode.removeChild(this.controlContainer);
|
||||
document.removeEventListener("click", this.onDocumentClick);
|
||||
this.map = undefined;
|
||||
}
|
||||
|
||||
onDocumentClick(event) {
|
||||
if (
|
||||
this.controlContainer &&
|
||||
!this.controlContainer.contains(event.target) &&
|
||||
this.mapStyleContainer &&
|
||||
this.styleButton
|
||||
) {
|
||||
this.mapStyleContainer.style.display = "none";
|
||||
this.styleButton.style.display = "block";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MapStyleControl.DEFAULT_STYLE = "default";
|
||||
MapStyleControl.DEFAULT_STYLES = [
|
||||
{
|
||||
title: "Default",
|
||||
style: "default",
|
||||
},
|
||||
];
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<div class="video-wrapper" :style="style">
|
||||
<video :key="source" ref="video" class="video-player" :height="height" :width="width" :autoplay="autoplay"
|
||||
:style="style" :poster="poster" :loop="loop" preload="auto" controls playsinline @click.stop
|
||||
@keydown.esc.stop.prevent="$emit('close')">
|
||||
:style="style" :poster="poster" :loop="loop" preload="auto" controls playsinline
|
||||
@click.stop @keydown.esc.stop.prevent="$emit('close')">
|
||||
<source :src="source">
|
||||
</video>
|
||||
</div>
|
||||
|
|
|
@ -38,7 +38,7 @@ Additional information can be found in our Developer Guide:
|
|||
@import url("viewer.css");
|
||||
@import url("tables.css");
|
||||
@import url("results.css");
|
||||
@import url("maps.css");
|
||||
@import url("places.css");
|
||||
@import url("labels.css");
|
||||
@import url("files.css");
|
||||
@import url("pages.css");
|
||||
|
@ -68,7 +68,7 @@ main {
|
|||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Page Overlay */
|
||||
/* Loading Animations */
|
||||
|
||||
#busy-overlay {
|
||||
display: none;
|
||||
|
|
|
@ -1,32 +1,49 @@
|
|||
/* Layout Breakpoints */
|
||||
|
||||
@media (min-width: 1750px) {
|
||||
/* Extra Large (XL) */
|
||||
.flex.xlg2 {
|
||||
flex-basis: 16.666666666666664%;
|
||||
flex-grow: 0;
|
||||
max-width: 16.666666666666664%;
|
||||
}
|
||||
.flex.xlg10 {
|
||||
flex-basis: 83.333333333333332%;
|
||||
flex-grow: 0;
|
||||
max-width: 83.333333333333332%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 2400px) {
|
||||
/* Extra Extra Large (XXL) */
|
||||
.flex.xxl1 {
|
||||
flex-basis: 8.333333333333332%;
|
||||
flex-grow: 0;
|
||||
max-width: 8.333333333333332%;
|
||||
}
|
||||
.flex.xxl11 {
|
||||
flex-basis: 91.66666666666667%;
|
||||
flex-grow: 0;
|
||||
max-width: 91.66666666666667%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 2550px) {
|
||||
.flex.xxxl1 {
|
||||
@media (min-width: 2800px) {
|
||||
/* Ultra Large (UL) */
|
||||
.flex.ul1 {
|
||||
flex-basis: 8.333333333333332%;
|
||||
flex-grow: 0;
|
||||
max-width: 8.333333333333332%;
|
||||
}
|
||||
.flex.ul11 {
|
||||
flex-basis: 91.66666666666667%;
|
||||
flex-grow: 0;
|
||||
max-width: 91.66666666666667%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Layout Widths */
|
||||
|
||||
|
||||
.width-sm, .width-md, .width-lg {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
@ -95,6 +112,13 @@
|
|||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.ra-2,
|
||||
.ra-2 > a,
|
||||
.rounded-2,
|
||||
.rounded-2 > a {
|
||||
border-radius: 2px !important;
|
||||
}
|
||||
|
||||
.ra-3,
|
||||
.ra-3 > a,
|
||||
.rounded-3,
|
||||
|
@ -137,6 +161,13 @@
|
|||
border-radius: 10px !important;
|
||||
}
|
||||
|
||||
.ra-24,
|
||||
.ra-24 > a,
|
||||
.rounded-24,
|
||||
.rounded-24 > a {
|
||||
border-radius: 24px !important;
|
||||
}
|
||||
|
||||
#photoprism div.v-dialog.v-dialog--fullscreen > div.v-card {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
@ -150,6 +181,30 @@
|
|||
border-radius: 5px 5px 0 0;
|
||||
}
|
||||
|
||||
/* Responsive Layout Styles */
|
||||
|
||||
@media only screen and (min-width: 960px) {
|
||||
.pa-1-md-and-up {
|
||||
padding: 4px!important;
|
||||
}
|
||||
|
||||
.pa-2-md-and-up {
|
||||
padding: 8px!important;
|
||||
}
|
||||
|
||||
.pa-3-md-and-up {
|
||||
padding: 16px!important;
|
||||
}
|
||||
|
||||
.pa-3-md-and-up {
|
||||
padding: 24px!important;
|
||||
}
|
||||
|
||||
.ra-4-table-md-and-up table.v-table {
|
||||
border-radius: 4px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Page Footer */
|
||||
|
||||
footer {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* Event Logs */
|
||||
|
||||
#photoprism .p-logs {
|
||||
color: white;
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
#photoprism .map-control {
|
||||
position: absolute;
|
||||
background: transparent;
|
||||
bottom: 35px;
|
||||
right: 30px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#photoprism.is-rtl .map-control {
|
||||
right: auto;
|
||||
left: 30px;
|
||||
}
|
||||
|
||||
#photoprism #map .marker {
|
||||
box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12) !important;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
border-color: #fff;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
display: block;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#photoprism .maplibregl-ctrl-attrib-inner a {
|
||||
color: #333 !important;
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
/* App Navigation */
|
||||
|
||||
#p-navigation {
|
||||
z-index: 10;
|
||||
}
|
||||
|
@ -120,6 +122,12 @@ nav .v-list__tile__title.title {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
#photoprism .p-photo-toolbar.embedded {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.p-user-box {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
|
@ -307,4 +315,4 @@ nav .v-list__tile__title.title {
|
|||
#photoprism #mobile-menu .menu-action:hover a i{
|
||||
fill: #ffffff;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
|
148
frontend/src/css/places.css
Normal file
148
frontend/src/css/places.css
Normal file
|
@ -0,0 +1,148 @@
|
|||
/* Maps & Places */
|
||||
|
||||
#photoprism .map-control {
|
||||
position: absolute;
|
||||
background: transparent;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#photoprism.is-rtl .map-control {
|
||||
left: auto;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
#photoprism .map-control .map-control-search {
|
||||
overflow: hidden;
|
||||
border-radius: 24px;
|
||||
}
|
||||
|
||||
#photoprism #map .marker {
|
||||
box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12) !important;
|
||||
background-color: rgba(23, 23, 23, 0.23);
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
display: block;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
border: 1px solid #ffffff99;
|
||||
}
|
||||
|
||||
#photoprism #map .cluster-marker {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: 1px;
|
||||
overflow: hidden;
|
||||
/* background-color: #ffffff99; */
|
||||
width: 100%;
|
||||
height: 100%
|
||||
}
|
||||
|
||||
#photoprism #map .counter-bubble {
|
||||
border-radius: 100%;
|
||||
position: absolute;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
color: #ffffff;
|
||||
top: -5px;
|
||||
right: -5px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0px 3px 1px -2px rgb(0 0 0 / 20%), 0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%);
|
||||
}
|
||||
|
||||
#photoprism #map .cluster-marker > div {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
#photoprism .maplibregl-ctrl-attrib-inner a {
|
||||
color: #333 !important;
|
||||
}
|
||||
|
||||
#photoprism .maplibregl-style-list {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#photoprism .maplibregl-ctrl-group .maplibregl-style-list button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
padding: 8px 8px 6px;
|
||||
text-align: right;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
#photoprism .maplibregl-style-list button.active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#photoprism .maplibregl-style-list button:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
#photoprism .maplibregl-style-list button + button {
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
#photoprism .maplibregl-style-switcher {
|
||||
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iNTQuODQ5cHgiIGhlaWdodD0iNTQuODQ5cHgiIHZpZXdCb3g9IjAgMCA1NC44NDkgNTQuODQ5IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1NC44NDkgNTQuODQ5OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PGc+PGc+PGc+PHBhdGggZD0iTTU0LjQ5NywzOS42MTRsLTEwLjM2My00LjQ5bC0xNC45MTcsNS45NjhjLTAuNTM3LDAuMjE0LTEuMTY1LDAuMzE5LTEuNzkzLDAuMzE5Yy0wLjYyNywwLTEuMjU0LTAuMTA0LTEuNzktMC4zMThsLTE0LjkyMS01Ljk2OEwwLjM1MSwzOS42MTRjLTAuNDcyLDAuMjAzLTAuNDY3LDAuNTI0LDAuMDEsMC43MTZMMjYuNTYsNTAuODFjMC40NzcsMC4xOTEsMS4yNTEsMC4xOTEsMS43MjksMEw1NC40ODgsNDAuMzNDNTQuOTY0LDQwLjEzOSw1NC45NjksMzkuODE3LDU0LjQ5NywzOS42MTR6Ii8+PHBhdGggZD0iTTU0LjQ5NywyNy41MTJsLTEwLjM2NC00LjQ5MWwtMTQuOTE2LDUuOTY2Yy0wLjUzNiwwLjIxNS0xLjE2NSwwLjMyMS0xLjc5MiwwLjMyMWMtMC42MjgsMC0xLjI1Ni0wLjEwNi0xLjc5My0wLjMyMWwtMTQuOTE4LTUuOTY2TDAuMzUxLDI3LjUxMmMtMC40NzIsMC4yMDMtMC40NjcsMC41MjMsMC4wMSwwLjcxNkwyNi41NiwzOC43MDZjMC40NzcsMC4xOSwxLjI1MSwwLjE5LDEuNzI5LDBsMjYuMTk5LTEwLjQ3OUM1NC45NjQsMjguMDM2LDU0Ljk2OSwyNy43MTYsNTQuNDk3LDI3LjUxMnoiLz48cGF0aCBkPSJNMC4zNjEsMTYuMTI1bDEzLjY2Miw1LjQ2NWwxMi41MzcsNS4wMTVjMC40NzcsMC4xOTEsMS4yNTEsMC4xOTEsMS43MjksMGwxMi41NDEtNS4wMTZsMTMuNjU4LTUuNDYzYzAuNDc3LTAuMTkxLDAuNDgtMC41MTEsMC4wMS0wLjcxNkwyOC4yNzcsNC4wNDhjLTAuNDcxLTAuMjA0LTEuMjM2LTAuMjA0LTEuNzA4LDBMMC4zNTEsMTUuNDFDLTAuMTIxLDE1LjYxNC0wLjExNiwxNS45MzUsMC4zNjEsMTYuMTI1eiIvPjwvZz48L2c+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjwvc3ZnPg==);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 70%;
|
||||
}
|
||||
|
||||
#photoprism .cluster-control {
|
||||
background: #2f3031;
|
||||
position: absolute;
|
||||
top: auto;
|
||||
z-index: 2;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
left: 4px;
|
||||
right: 4px;
|
||||
border-top-left-radius: 16px;
|
||||
border-top-right-radius: 16px;
|
||||
max-height: 90vh;
|
||||
overflow: hidden;
|
||||
box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px;
|
||||
transition: all 650ms cubic-bezier(0.32, 1, 0.23, 1) 0ms;
|
||||
}
|
||||
|
||||
#photoprism .cluster-control-container {
|
||||
min-height: 40vh;
|
||||
}
|
||||
|
||||
#photoprism .cluster-control .p-photos {
|
||||
position: absolute;
|
||||
top: 45px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
@media only screen and (min-height: 1500px) {
|
||||
#photoprism .cluster-control-container {
|
||||
min-height: 45vh;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 599px) {
|
||||
#photoprism .cluster-control {
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#photoprism .cluster-control-container {
|
||||
min-height: 66vh;
|
||||
}
|
||||
}
|
|
@ -198,112 +198,128 @@ body.chrome #photoprism .search-results .result {
|
|||
}
|
||||
|
||||
#photoprism .list-view {
|
||||
box-shadow: 0 0 0 0 rgba(0,0,0,.2),0 0 0 0 rgba(0,0,0,.14),0 0 0 0 rgba(0,0,0,.12) !important;
|
||||
box-shadow: 0 0 0 0 rgba(0, 0, 0, .2), 0 0 0 0 rgba(0, 0, 0, .14), 0 0 0 0 rgba(0, 0, 0, .12) !important;
|
||||
}
|
||||
|
||||
#photoprism .cards-view .result,
|
||||
#photoprism .mosaic-view .result
|
||||
{
|
||||
-webkit-transition-duration: 15ms !important;
|
||||
-moz-transition-duration: 15ms !important;
|
||||
-o-transition-duration: 15ms !important;
|
||||
transition-duration: 15ms !important;
|
||||
margin: 4px !important;
|
||||
box-shadow: 0 0 0 0 rgba(0, 0, 0, .2), 0 0 0 0 rgba(0, 0, 0, .14), 0 0 0 0 rgba(0, 0, 0, .12) !important;
|
||||
border-radius: 6px;
|
||||
#photoprism .mosaic-view .result {
|
||||
transition: none;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
#photoprism .cards-view .result {
|
||||
margin: 2px !important;
|
||||
}
|
||||
|
||||
#photoprism .mosaic-view .result {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
#photoprism .search-results .image-container {
|
||||
aspect-ratio: 1;
|
||||
contain: layout paint style size;
|
||||
aspect-ratio: 1;
|
||||
contain: layout paint style size;
|
||||
}
|
||||
|
||||
#photoprism .cards-view .result .image,
|
||||
#photoprism .list-view .result .image,
|
||||
#photoprism .mosaic-view .result.image
|
||||
{
|
||||
position: relative;
|
||||
user-select: none;
|
||||
aspect-ratio: 1;
|
||||
background-position: center center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
overflow: hidden;
|
||||
#photoprism .mosaic-view .result.image {
|
||||
position: relative;
|
||||
user-select: none;
|
||||
aspect-ratio: 1;
|
||||
background-position: center center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#photoprism .cards-view .result .image button,
|
||||
#photoprism .list-view .result .image button,
|
||||
#photoprism .mosaic-view .result.image button {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
padding: 0;
|
||||
transition: background-color .3s cubic-bezier(.25,.8,.5,1);
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
padding: 0;
|
||||
transition: background-color .3s cubic-bezier(.25, .8, .5, 1);
|
||||
}
|
||||
|
||||
#photoprism .cards-view .result .image button {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
#photoprism .list-view .result .image button {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
#photoprism .cards-view .result .image button:hover,
|
||||
#photoprism .list-view .result .image button:hover,
|
||||
#photoprism .mosaic-view .result.image button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.12);
|
||||
background-color: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
#photoprism .mosaic-view .result.image i,
|
||||
#photoprism .list-view .result .image i,
|
||||
#photoprism .cards-view .result .image i {
|
||||
color: #e2e2e2;
|
||||
font-size: 24px;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-family: Material Icons;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
color: #e2e2e2;
|
||||
font-size: 24px;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-family: Material Icons;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#photoprism .cards-view .result .caption {
|
||||
hyphens: auto;
|
||||
word-break: break-word;
|
||||
hyphens: auto;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
#photoprism .cards-view .result .card-details i {
|
||||
font-size: 14px;
|
||||
display: inline-block;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-family: Material Icons;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
vertical-align: text-bottom;
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
display: inline-block;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-family: Material Icons;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
vertical-align: text-bottom;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
#photoprism .cards-view .result.placeholder .card-details i {
|
||||
width: 14px;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
#photoprism .cards-view .result.is-selected {
|
||||
margin: 7px !important;
|
||||
}
|
||||
|
||||
#photoprism .cards-view .result.is-selected,
|
||||
#photoprism .mosaic-view .result.is-selected {
|
||||
margin: 1px !important;
|
||||
box-shadow: 0 6px 6px -3px rgba(0, 0, 0, .2), 0 10px 14px 1px rgba(0, 0, 0, .14), 0 4px 18px 3px rgba(0, 0, 0, .12) !important;
|
||||
margin: 9px !important;
|
||||
}
|
||||
|
||||
#photoprism .list-view .result.is-selected .image {
|
||||
margin: 2px !important;
|
||||
}
|
||||
|
||||
#photoprism .cards-view .result.is-selected .card-details {
|
||||
padding-left: 14px !important;
|
||||
padding-right: 14px !important;
|
||||
}
|
||||
|
||||
#photoprism .cards-view .result.is-selected .card-details .caption {
|
||||
font-size: 0.85em !important;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
#photoprism .cards-view .input-select,
|
||||
|
@ -515,28 +531,27 @@ body.chrome #photoprism .search-results .result {
|
|||
|
||||
/* old browser support */
|
||||
@supports not (aspect-ratio: 1) {
|
||||
/* elements with aspect-ratio 1 and without margin */
|
||||
#photoprism .search-results .image-container,
|
||||
#photoprism .cards-view .result .image,
|
||||
#photoprism .list-view .result .image,
|
||||
#photoprism .list-view .result.image,
|
||||
#photoprism .mosaic-view .result.is-selected {
|
||||
height: 0;
|
||||
padding-bottom: 100%
|
||||
}
|
||||
/* elements with aspect-ratio 1 and without margin */
|
||||
#photoprism .search-results .image-container,
|
||||
#photoprism .cards-view .result .image,
|
||||
#photoprism .list-view .result .image,
|
||||
#photoprism .list-view .result.image,
|
||||
#photoprism .mosaic-view .result.is-selected {
|
||||
height: 0;
|
||||
padding-bottom: 100%
|
||||
}
|
||||
|
||||
/* elements with aspect-ratio 1 and with margin */
|
||||
#photoprism .mosaic-view .result
|
||||
{
|
||||
height: 0;
|
||||
/*
|
||||
8px is required because this aspect-ratio-fallback doesn't take
|
||||
margins into consideration
|
||||
*/
|
||||
padding-bottom: calc(100% - 8px)
|
||||
}
|
||||
/* elements with aspect-ratio 1 and with margin */
|
||||
#photoprism .mosaic-view .result {
|
||||
height: 0;
|
||||
/*
|
||||
8px is required because this aspect-ratio-fallback doesn't take
|
||||
margins into consideration
|
||||
*/
|
||||
padding-bottom: calc(100% - 8px)
|
||||
}
|
||||
|
||||
#photoprism .live-player video {
|
||||
margin-top: 100%;
|
||||
}
|
||||
#photoprism .live-player video {
|
||||
margin-top: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,4 +38,14 @@
|
|||
#photoprism.is-rtl .v-toolbar__extension > :last-child.v-btn--icon {
|
||||
margin-left: -6px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
#photoprism .v-btn.v-btn--depressed.v-btn--small .v-icon--right {
|
||||
font-size: 16px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
#photoprism .v-btn.v-btn--depressed.v-btn--small .v-icon--left {
|
||||
font-size: 16px;
|
||||
margin-right: 8px;
|
||||
}
|
|
@ -1,16 +1,18 @@
|
|||
@import url("themes/grayscale.css");
|
||||
@import url("themes/shadow.css");
|
||||
@import url("themes/yellowstone.css");
|
||||
@import url("themes/abyss.css");
|
||||
@import url("themes/vanta.css");
|
||||
@import url("themes/neon.css");
|
||||
@import url("themes/gemstone.css");
|
||||
@import url("themes/carbon.css");
|
||||
@import url("themes/nordic.css");
|
||||
@import url("themes/lavender.css");
|
||||
@import url("themes/default.css");
|
||||
@import url("themes/abyss.css");
|
||||
@import url("themes/carbon.css");
|
||||
@import url("themes/chrome.css");
|
||||
@import url("themes/gemstone.css");
|
||||
@import url("themes/grayscale.css");
|
||||
@import url("themes/lavender.css");
|
||||
@import url("themes/mint.css");
|
||||
@import url("themes/neon.css");
|
||||
@import url("themes/nordic.css");
|
||||
@import url("themes/shadow.css");
|
||||
@import url("themes/vanta.css");
|
||||
@import url("themes/yellowstone.css");
|
||||
|
||||
/* General Styles */
|
||||
/* Input Style Overrides */
|
||||
|
||||
:-webkit-autofill,
|
||||
:-webkit-autofill:hover,
|
||||
|
@ -21,90 +23,15 @@ input:-webkit-autofill:focus {
|
|||
transition: background-color 9999s !important;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism .v-tabs .v-badge__badge {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism .theme--light.v-table thead th {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
body.dark-theme .card {
|
||||
background-color: #1a1a1a;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism .v-table .theme--light.v-text-field--solo>.v-input__control>.v-input__slot {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
body.dark-theme .theme--light.v-list .v-list__tile--highlighted,
|
||||
body.dark-theme .theme--light.v-list a:hover {
|
||||
background: rgba(255,255,255,0.3) !important;
|
||||
}
|
||||
|
||||
body.dark-theme .v-input input::placeholder {
|
||||
color: rgba(255,255,255,0.5) !important;
|
||||
}
|
||||
|
||||
.v-text-field>.v-input__control>.v-input__slot:after,
|
||||
.v-text-field>.v-input__control>.v-input__slot:before {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/*
|
||||
body.dark-theme .v-text-field.v-text-field--enclosed .v-text-field__details {
|
||||
padding-left: 0;
|
||||
}*/
|
||||
|
||||
.theme--light.v-list .v-list__tile--highlighted,
|
||||
.theme--light.v-list a:hover {
|
||||
background: rgba(155,155,155,0.3) !important;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism .map-control .theme--light.v-input:not(.v-input--is-disabled) i,
|
||||
body.dark-theme #photoprism .map-control .theme--light.v-input:not(.v-input--is-disabled) input {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
body:not(.dark-theme) .theme--dark.v-btn.v-btn--disabled:not(.v-btn--icon):not(.v-btn--flat):not(.v-btn--outline) {
|
||||
color: rgb(51, 51, 51)!important;
|
||||
background-color: #d9dadc!important;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism .theme--light.v-chip,
|
||||
body.dark-theme #photoprism .v-card .theme--light.v-text-field--box>.v-input__control>.v-input__slot {
|
||||
background: #00000033;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism,
|
||||
body.dark-theme #photoprism .p-page a,
|
||||
body.dark-theme #photoprism .v-datatable a,
|
||||
body.dark-theme #photoprism .theme--light.v-expansion-panel .v-expansion-panel__container,
|
||||
body.dark-theme #photoprism .theme--light.v-tabs__bar .v-tabs__div,
|
||||
body.dark-theme #photoprism .theme--light {
|
||||
color: #ffffff;
|
||||
caret-color: #ffffff;
|
||||
stroke: #ffffff;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism .theme--light.v-label {
|
||||
color: #ffffffaa;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism .theme--light.v-select .v-select__selections {
|
||||
color: #ffffffee;
|
||||
}
|
||||
|
||||
#photoprism .v-select.v-text-field--box .v-select__selections {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
/* Exceptions */
|
||||
|
||||
#photoprism .theme--light.v-text-field--solo.background-inherit>.v-input__control>.v-input__slot {
|
||||
background: inherit;
|
||||
box-shadow: none;
|
||||
|
|
|
@ -33,8 +33,8 @@ body.dark-theme.theme-abyss,
|
|||
background: #333;
|
||||
}
|
||||
|
||||
#photoprism.theme-abyss .theme--light.v-text-field--solo > .v-input__control > .v-input__slot {
|
||||
background: #262626;
|
||||
#photoprism.theme-abyss .theme--light.v-text-field--solo>.v-input__control>.v-input__slot {
|
||||
background: rgba(250, 250, 255, 0.1);
|
||||
}
|
||||
|
||||
#photoprism.theme-abyss .theme--light.v-input--selection-controls.v-input--is-disabled .v-icon {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Carbon Theme */
|
||||
/* Carbon Dark Theme */
|
||||
|
||||
body.dark-theme.theme-carbon,
|
||||
.theme-carbon .v-content__wrap,
|
||||
|
|
146
frontend/src/css/themes/chrome.css
Normal file
146
frontend/src/css/themes/chrome.css
Normal file
|
@ -0,0 +1,146 @@
|
|||
/* Chrome Dark Theme */
|
||||
|
||||
body.dark-theme.theme-chrome,
|
||||
.theme-chrome .v-content__wrap,
|
||||
.theme-chrome .p-page,
|
||||
.theme-chrome .form,
|
||||
.theme-chrome .v-content {
|
||||
background: #1d1d1d !important;
|
||||
}
|
||||
|
||||
#photoprism.theme-chrome .theme--light.v-small-dialog__content,
|
||||
#photoprism.theme-chrome .theme--light.v-sheet,
|
||||
#photoprism.theme-chrome .theme--light.v-card,
|
||||
.theme-chrome .application.theme--light {
|
||||
background: #202020;
|
||||
}
|
||||
|
||||
#photoprism.container.theme-chrome {
|
||||
background-image: linear-gradient(160deg, #808080 0%, #262626 100%);
|
||||
}
|
||||
|
||||
#photoprism.theme-chrome .theme--light .v-table {
|
||||
background: #262626;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism.theme-chrome,
|
||||
body.dark-theme #photoprism.theme-chrome .v-datatable a,
|
||||
body.dark-theme #photoprism.theme-chrome .theme--light.v-expansion-panel .v-expansion-panel__container,
|
||||
body.dark-theme #photoprism.theme-chrome .theme--light.v-tabs__bar .v-tabs__div {
|
||||
color: #ffffff;
|
||||
caret-color: #ffffff;
|
||||
stroke: #ffffff;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism.theme-chrome .p-page a,
|
||||
body.dark-theme #photoprism.theme-chrome .theme--light,
|
||||
body.dark-theme #photoprism.theme-chrome #p-navigation .navigation-menu.theme--dark.v-list,
|
||||
body.dark-theme #photoprism.theme-chrome #p-navigation .navigation-menu .theme--dark.v-icon {
|
||||
color: #d8d8d8;
|
||||
caret-color: #d8d8d8;
|
||||
stroke: #d8d8d8;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism.theme-chrome .v-input--selection-controls__input .theme--light {
|
||||
color: #8e8e8e;
|
||||
caret-color: #8e8e8e;
|
||||
stroke: #8e8e8e;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism.theme-chrome #p-navigation .navigation-menu .theme--dark.v-icon {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#photoprism.theme-chrome .theme--light.v-table thead th,
|
||||
#photoprism.theme-chrome .theme--light.v-table tbody td {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#photoprism.theme-chrome .theme--light.v-table tbody tr:hover {
|
||||
background: #2b2b2b;
|
||||
}
|
||||
|
||||
#photoprism.theme-chrome .theme--light.v-text-field--solo>.v-input__control>.v-input__slot,
|
||||
#photoprism.theme-chrome .theme--light.v-text-field--solo>.v-input__control>.v-input__slot {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
#photoprism.theme-chrome .secondary--text {
|
||||
color: rgba(255, 255, 255, 0.2)!important;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism.theme-chrome #map .secondary-dark.theme--light,
|
||||
body.dark-theme #photoprism.theme-chrome #map .secondary-dark--text {
|
||||
color: #000000!important;
|
||||
caret-color: #000000!important;
|
||||
stroke: #000000!important;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism.theme-chrome .map-control .theme--light {
|
||||
color: #757575;
|
||||
caret-color: #757575;
|
||||
stroke: #757575;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism.theme-chrome .v-card .theme--light.v-text-field--box>.v-input__control>.v-input__slot {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
#photoprism.theme-chrome .theme--light.v-input--selection-controls.v-input--is-disabled .v-icon {
|
||||
color: #999 !important;
|
||||
}
|
||||
|
||||
#photoprism.theme-chrome .theme--light.v-input--selection-controls.v-input--is-disabled .v-icon,
|
||||
#photoprism.theme-chrome .theme--light.v-input--is-disabled .v-label,
|
||||
#photoprism.theme-chrome .theme--light.v-input--is-disabled input,
|
||||
#photoprism.theme-chrome .theme--light.v-input--is-disabled textarea {
|
||||
color: #aaaaaa;
|
||||
}
|
||||
|
||||
#photoprism.theme-chrome .theme--light.v-list {
|
||||
background: #232425;
|
||||
}
|
||||
|
||||
#photoprism.theme-chrome a.text-link {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
#photoprism.theme-chrome .footer .body-link {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
#photoprism.theme-chrome .theme--light.v-list .v-list__tile__sub-title,
|
||||
#photoprism.theme-chrome .accent--text {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
#photoprism.theme-chrome .theme--light.v-input:not(.v-input--is-disabled) input,
|
||||
#photoprism.theme-chrome .theme--light.v-input:not(.v-input--is-disabled) textarea {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#photoprism.theme-chrome .theme--light.v-btn.v-btn--disabled,
|
||||
#photoprism.theme-chrome .theme--light.v-btn.v-btn--disabled .v-btn__loading,
|
||||
#photoprism.theme-chrome .theme--light.v-btn.v-btn--disabled .v-icon {
|
||||
color: #999 !important;
|
||||
}
|
||||
|
||||
#photoprism.theme-chrome .theme--light.v-list .v-list__tile__mask {
|
||||
color: #cccccc;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#photoprism.theme-chrome .cards-view .card.is-selected .card-details {
|
||||
color: #fff;
|
||||
background-color: #5C5C5C;
|
||||
}
|
||||
|
||||
#photoprism.theme-chrome .cards-view .card.is-selected,
|
||||
#photoprism.theme-chrome .cards-view .card.is-selected .card-background {
|
||||
background-color: #5C5C5C !important;
|
||||
}
|
||||
|
||||
#photoprism.theme-chrome .cards-view .card.is-selected .card-details .v-icon {
|
||||
color: #fff;
|
||||
}
|
||||
|
|
@ -1,7 +1,75 @@
|
|||
/* Default Theme */
|
||||
/* Component Color Defaults */
|
||||
|
||||
#photoprism.theme-default .theme--light.v-text-field--solo>.v-input__control>.v-input__slot,
|
||||
#photoprism.theme-default .theme--light.v-text-field--solo>.v-input__control>.v-input__slot {
|
||||
.card {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
body.dark-theme .card {
|
||||
background-color: #1a1a1a;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism .v-table .theme--light.v-text-field--solo>.v-input__control>.v-input__slot {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
body.dark-theme .theme--light.v-list .v-list__tile--highlighted,
|
||||
body.dark-theme .theme--light.v-list a:hover {
|
||||
background: rgba(255,255,255,0.3) !important;
|
||||
}
|
||||
|
||||
body.dark-theme .v-input input::placeholder {
|
||||
color: rgba(255,255,255,0.5) !important;
|
||||
}
|
||||
|
||||
body.dark-theme .theme--light .v-table {
|
||||
background: #303030;
|
||||
}
|
||||
|
||||
body.dark-theme .theme--light.v-table tbody tr:hover {
|
||||
background: #343434;
|
||||
}
|
||||
|
||||
.theme--light.v-list .v-list__tile--highlighted,
|
||||
.theme--light.v-list a:hover {
|
||||
background: rgba(155,155,155,0.3) !important;
|
||||
}
|
||||
|
||||
body:not(.dark-theme) .theme--dark.v-btn.v-btn--disabled:not(.v-btn--icon):not(.v-btn--flat):not(.v-btn--outline) {
|
||||
color: rgb(51, 51, 51)!important;
|
||||
background-color: #d9dadc!important;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism .theme--light.v-chip,
|
||||
body.dark-theme #photoprism .v-card .theme--light.v-text-field--box>.v-input__control>.v-input__slot {
|
||||
background: #00000033;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism,
|
||||
body.dark-theme #photoprism .p-page a,
|
||||
body.dark-theme #photoprism .v-datatable a,
|
||||
body.dark-theme #photoprism .theme--light.v-expansion-panel .v-expansion-panel__container,
|
||||
body.dark-theme #photoprism .theme--light.v-tabs__bar .v-tabs__div,
|
||||
body.dark-theme #photoprism .theme--light {
|
||||
color: #ffffff;
|
||||
caret-color: #ffffff;
|
||||
stroke: #ffffff;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism .theme--light.v-label {
|
||||
color: #ffffffaa;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism .theme--light.v-select .v-select__selections {
|
||||
color: #ffffffee;
|
||||
}
|
||||
|
||||
body.dark-theme .theme--light.v-small-dialog__content,
|
||||
body.dark-theme .theme--light.v-sheet,
|
||||
body.dark-theme .theme--light.v-card {
|
||||
background: #2f3031;
|
||||
}
|
||||
|
||||
body.dark-theme .theme--light.v-text-field--solo>.v-input__control>.v-input__slot {
|
||||
background: rgba(250, 250, 255, 0.1);
|
||||
}
|
||||
|
||||
|
@ -20,7 +88,10 @@ body.dark-theme #photoprism .p-page a,
|
|||
body.dark-theme #photoprism .v-datatable a,
|
||||
body.dark-theme #photoprism .theme--light.v-expansion-panel .v-expansion-panel__container,
|
||||
body.dark-theme #photoprism .theme--light.v-tabs__bar .v-tabs__div,
|
||||
body.dark-theme #photoprism .theme--light {
|
||||
body.dark-theme #photoprism .theme--light,
|
||||
body.dark-theme #photoprism .theme--light.v-table thead th,
|
||||
body.dark-theme .theme--light.v-table thead th,
|
||||
body.dark-theme .theme--light.v-table tbody td {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
|
@ -36,6 +107,12 @@ body.dark-theme #photoprism .theme--light.v-select .v-select__selections {
|
|||
padding-top: 20px;
|
||||
}
|
||||
|
||||
body.dark-theme #photoprism .v-tabs .v-badge__badge {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/* Default Theme */
|
||||
|
||||
body.dark-theme.theme-default,
|
||||
.theme-default .v-content__wrap,
|
||||
.theme-default .p-page,
|
||||
|
@ -51,10 +128,6 @@ body.dark-theme.theme-default,
|
|||
background: #2f3031;
|
||||
}
|
||||
|
||||
#photoprism.theme-default .theme--light .v-table {
|
||||
background: #37393a;
|
||||
}
|
||||
|
||||
#photoprism.container.theme-default {
|
||||
background-image: linear-gradient(160deg, #808080 0%, #262626 100%);
|
||||
}
|
||||
|
@ -68,6 +141,10 @@ body.dark-theme.theme-default,
|
|||
stroke: rgba(196, 241, 229, 0.3);
|
||||
}
|
||||
|
||||
#photoprism.theme-default .theme--light .v-table {
|
||||
background: #37393a;
|
||||
}
|
||||
|
||||
#photoprism.theme-default .theme--light.v-table thead th,
|
||||
#photoprism.theme-default .theme--light.v-table tbody td {
|
||||
color: #fff;
|
||||
|
@ -81,6 +158,11 @@ body.dark-theme.theme-default,
|
|||
color: #999 !important;
|
||||
}
|
||||
|
||||
#photoprism.theme-default .theme--light.v-text-field--solo>.v-input__control>.v-input__slot,
|
||||
#photoprism.theme-default .theme--light.v-text-field--solo>.v-input__control>.v-input__slot {
|
||||
background: rgba(250, 250, 255, 0.1);
|
||||
}
|
||||
|
||||
#photoprism.theme-default .theme--light.v-input--selection-controls.v-input--is-disabled .v-icon,
|
||||
#photoprism.theme-default .theme--light.v-input--is-disabled .v-label,
|
||||
#photoprism.theme-default .theme--light.v-input--is-disabled input,
|
||||
|
@ -123,12 +205,12 @@ body.dark-theme.theme-default,
|
|||
|
||||
#photoprism.theme-default .cards-view .card.is-selected .card-details {
|
||||
color: #fff;
|
||||
background-color: #4b4e4f;
|
||||
background-color: #636869;
|
||||
}
|
||||
|
||||
#photoprism.theme-default .cards-view .card.is-selected,
|
||||
#photoprism.theme-default .cards-view .card.is-selected .card-background {
|
||||
background-color: #4b4e4f !important;
|
||||
background-color: #636869 !important;
|
||||
}
|
||||
|
||||
#photoprism.theme-default .cards-view .card.is-selected .card-details .v-icon {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Gemstone Theme */
|
||||
/* Gemstone Dark Theme */
|
||||
|
||||
body.dark-theme.theme-gemstone,
|
||||
.theme-gemstone .v-content__wrap,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Grayscale Theme */
|
||||
/* Grayscale Dark Theme */
|
||||
|
||||
body.dark-theme.theme-grayscale {
|
||||
background: #525252 !important;
|
||||
|
|
92
frontend/src/css/themes/mint.css
Normal file
92
frontend/src/css/themes/mint.css
Normal file
|
@ -0,0 +1,92 @@
|
|||
/* Mint Dark Theme */
|
||||
|
||||
body.dark-theme.theme-mint,
|
||||
.theme-mint .v-content__wrap,
|
||||
.theme-mint .p-page,
|
||||
.theme-mint .form,
|
||||
.theme-mint .v-content {
|
||||
background: #121212 !important;
|
||||
}
|
||||
|
||||
#photoprism.theme-mint .theme--light.v-small-dialog__content,
|
||||
#photoprism.theme-mint .theme--light.v-sheet,
|
||||
#photoprism.theme-mint .theme--light.v-card,
|
||||
.theme-mint .application.theme--light {
|
||||
background: #1e1e1e;
|
||||
}
|
||||
|
||||
#photoprism.container.theme-mint {
|
||||
background-image: linear-gradient(160deg, #808080 0%, #262626 100%);
|
||||
}
|
||||
|
||||
#photoprism.theme-mint .theme--light .v-table {
|
||||
background: #1a1a1a;
|
||||
}
|
||||
|
||||
#photoprism.theme-mint .theme--light.v-table thead th,
|
||||
#photoprism.theme-mint .theme--light.v-table tbody td {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#photoprism.theme-mint .theme--light.v-table tbody tr:hover {
|
||||
background: #2b2b2b;
|
||||
}
|
||||
|
||||
#photoprism.theme-mint .theme--light.v-input--selection-controls.v-input--is-disabled .v-icon {
|
||||
color: #999 !important;
|
||||
}
|
||||
|
||||
#photoprism.theme-mint .theme--light.v-input--selection-controls.v-input--is-disabled .v-icon,
|
||||
#photoprism.theme-mint .theme--light.v-input--is-disabled .v-label,
|
||||
#photoprism.theme-mint .theme--light.v-input--is-disabled input,
|
||||
#photoprism.theme-mint .theme--light.v-input--is-disabled textarea {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
#photoprism.theme-mint .theme--light.v-list {
|
||||
background: #232425;
|
||||
}
|
||||
|
||||
#photoprism.theme-mint a.text-link {
|
||||
color: #f6f7e8 !important;
|
||||
}
|
||||
|
||||
#photoprism.theme-mint .footer .body-link {
|
||||
color: #f6f7e8 !important;
|
||||
}
|
||||
|
||||
#photoprism.theme-mint .theme--light.v-list .v-list__tile__sub-title,
|
||||
#photoprism.theme-mint .accent--text {
|
||||
color: #2bb14c !important;
|
||||
}
|
||||
|
||||
#photoprism.theme-mint .theme--light.v-input:not(.v-input--is-disabled) input,
|
||||
#photoprism.theme-mint .theme--light.v-input:not(.v-input--is-disabled) textarea {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#photoprism.theme-mint .theme--light.v-btn.v-btn--disabled,
|
||||
#photoprism.theme-mint .theme--light.v-btn.v-btn--disabled .v-btn__loading,
|
||||
#photoprism.theme-mint .theme--light.v-btn.v-btn--disabled .v-icon {
|
||||
color: #999 !important;
|
||||
}
|
||||
|
||||
#photoprism.theme-mint .theme--light.v-list .v-list__tile__mask {
|
||||
color: #cccccc;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#photoprism.theme-mint .cards-view .card.is-selected .card-details {
|
||||
color: #fff;
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
#photoprism.theme-mint .cards-view .card.is-selected,
|
||||
#photoprism.theme-mint .cards-view .card.is-selected .card-background {
|
||||
background-color: #333333 !important;
|
||||
}
|
||||
|
||||
#photoprism.theme-mint .cards-view .card.is-selected .card-details .v-icon {
|
||||
color: #fff;
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
/* Neon Theme */
|
||||
/* Neon Dark Theme */
|
||||
|
||||
.theme-neon .v-snack.v-snack--active.v-snack--bottom,
|
||||
.theme-neon .v-speed-dial .v-btn--floating.v-btn--small {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Shadow Theme */
|
||||
/* Shadow Dark Theme */
|
||||
|
||||
body.dark-theme.theme-shadow {
|
||||
background: #444 !important;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Vanta Theme */
|
||||
/* Vanta Dark Theme */
|
||||
|
||||
body.dark-theme.theme-vanta {
|
||||
background: #212121 !important;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Yellowstone Theme */
|
||||
/* Yellowstone Dark Theme */
|
||||
|
||||
body.dark-theme.theme-yellowstone,
|
||||
.theme-yellowstone .v-content__wrap,
|
||||
|
|
|
@ -39,6 +39,10 @@ button.v-btn.compact {
|
|||
height: 34px;
|
||||
}
|
||||
|
||||
.caption.filename {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Line Height */
|
||||
|
||||
.lh-15 {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background-color: rgba(0,0,0,0.66);
|
||||
background-color: rgba(0,0,0,0.72);
|
||||
}
|
||||
|
||||
#photoprism .video-wrapper {
|
||||
|
|
|
@ -86,7 +86,7 @@
|
|||
>
|
||||
</v-checkbox>
|
||||
</v-flex>
|
||||
<v-flex v-if="featExperimental && featPrivate" sm3 pa-2>
|
||||
<v-flex v-if="experimental && featPrivate" sm3 pa-2>
|
||||
<v-checkbox
|
||||
v-model="model.Private"
|
||||
:disabled="disabled"
|
||||
|
@ -134,8 +134,8 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
featExperimental: this.$config.get("experimental") && !this.$config.ce(),
|
||||
featPrivate: this.$config.feature("private"),
|
||||
experimental: this.$config.get("experimental") && !this.$config.ce(),
|
||||
disabled: !this.$config.allow("albums", "manage"),
|
||||
model: new Album(),
|
||||
growDesc: false,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<v-dialog :value="show" lazy persistent max-width="350" class="p-photo-delete-dialog" @keydown.esc="cancel">
|
||||
<v-dialog :value="show" lazy persistent max-width="360" class="p-photo-delete-dialog" @keydown.esc="cancel">
|
||||
<v-card raised elevation="24">
|
||||
<v-container fluid class="pb-2 pr-2 pl-2">
|
||||
<v-layout row wrap>
|
||||
|
@ -7,18 +7,25 @@
|
|||
<v-icon size="54" color="secondary-dark lighten-1">delete_outline</v-icon>
|
||||
</v-flex>
|
||||
<v-flex xs9 text-xs-left align-self-center>
|
||||
<div class="subheading pr-1">
|
||||
<div v-if="text === ''" class="subheading pr-1">
|
||||
<translate>Are you sure you want to permanently delete these pictures?</translate>
|
||||
</div>
|
||||
<div v-else class="subheading pr-1">
|
||||
{{ text }}
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs12 text-xs-right class="pt-3">
|
||||
<v-btn depressed color="secondary-light" class="action-cancel" @click.stop="cancel">
|
||||
<translate key="Cancel">Cancel</translate>
|
||||
</v-btn>
|
||||
<v-btn color="primary-button" depressed dark class="action-confirm"
|
||||
<v-btn v-if="action === ''" color="primary-button" depressed dark class="action-confirm"
|
||||
@click.stop="confirm">
|
||||
<translate key="Delete">Delete</translate>
|
||||
</v-btn>
|
||||
<v-btn v-else color="primary-button" depressed dark class="action-confirm"
|
||||
@click.stop="confirm">
|
||||
{{ action }}
|
||||
</v-btn>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-container>
|
||||
|
@ -30,6 +37,14 @@ export default {
|
|||
name: 'PPhotoDeleteDialog',
|
||||
props: {
|
||||
show: Boolean,
|
||||
text: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
action: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
|
|
|
@ -24,12 +24,11 @@
|
|||
</v-toolbar>
|
||||
<v-tabs
|
||||
v-model="active"
|
||||
flat
|
||||
grow
|
||||
flat grow
|
||||
class="form"
|
||||
color="secondary"
|
||||
slider-color="secondary-dark"
|
||||
:height="$vuetify.breakpoint.smAndDown ? 48 : 64"
|
||||
class="form"
|
||||
>
|
||||
<v-tab id="tab-details" ripple>
|
||||
<v-icon v-if="$vuetify.breakpoint.smAndDown" :title="$gettext('Details')">edit</v-icon>
|
||||
|
@ -82,7 +81,7 @@
|
|||
</v-tab>
|
||||
|
||||
<v-tabs-items touchless>
|
||||
<v-tab-item>
|
||||
<v-tab-item lazy>
|
||||
<p-tab-photo-details :key="uid" ref="details" :model="model" :uid="uid"
|
||||
@close="close" @prev="prev" @next="next"></p-tab-photo-details>
|
||||
</v-tab-item>
|
||||
|
|
|
@ -1,412 +1,408 @@
|
|||
<template>
|
||||
<div class="p-tab p-tab-photo-details">
|
||||
<v-container fluid>
|
||||
<v-form ref="form" lazy-validation
|
||||
dense class="p-form-photo-details-meta" accept-charset="UTF-8"
|
||||
@submit.prevent="save">
|
||||
<v-layout row wrap align-top fill-height>
|
||||
<v-flex
|
||||
class="p-photo pa-2"
|
||||
xs12 sm4 md2
|
||||
>
|
||||
<v-card tile
|
||||
class="ma-0 elevation-0"
|
||||
:title="model.Title">
|
||||
<v-img v-touch="{left, right}"
|
||||
:src="model.thumbnailUrl('tile_500')"
|
||||
aspect-ratio="1"
|
||||
class="card darken-1 elevation-0 clickable"
|
||||
@click.exact="openPhoto()"
|
||||
>
|
||||
</v-img>
|
||||
<v-form ref="form" lazy-validation
|
||||
dense class="p-form-photo-details-meta" accept-charset="UTF-8"
|
||||
@submit.prevent="save">
|
||||
<v-layout class="pa-2" row wrap align-top fill-height>
|
||||
<v-flex class="pa-2 p-photo" xs12 sm4 md2 fill-height>
|
||||
<v-card tile
|
||||
class="pa-0 ma-0 elevation-0"
|
||||
:title="model.Title">
|
||||
<v-img v-touch="{left, right}"
|
||||
:src="model.thumbnailUrl('tile_500')"
|
||||
aspect-ratio="1"
|
||||
class="card darken-1 elevation-0 clickable"
|
||||
@click.exact="openPhoto()"
|
||||
>
|
||||
</v-img>
|
||||
|
||||
</v-card>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm8 md10 fill-height>
|
||||
<v-layout row wrap>
|
||||
<v-flex xs12 lg6 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.Title"
|
||||
:append-icon="model.TitleSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:rules="[textRule]"
|
||||
hide-details box flat
|
||||
:label="$pgettext('Photo', 'Title')"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
browser-autocomplete="off"
|
||||
class="input-title"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
</v-card>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm8 md10 fill-height>
|
||||
<v-layout row wrap>
|
||||
<v-flex xs12 lg6 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.Title"
|
||||
:append-icon="model.TitleSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:rules="[textRule]"
|
||||
hide-details box flat
|
||||
:label="$pgettext('Photo', 'Title')"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
browser-autocomplete="off"
|
||||
class="input-title"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs4 md1 pa-2>
|
||||
<v-autocomplete
|
||||
v-model="model.Day"
|
||||
:append-icon="model.TakenSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:error="invalidDate"
|
||||
:label="$gettext('Day')"
|
||||
browser-autocomplete="off"
|
||||
hide-details box flat hide-no-data
|
||||
color="secondary-dark"
|
||||
:items="options.Days()"
|
||||
class="input-day"
|
||||
@change="updateTime">
|
||||
</v-autocomplete>
|
||||
</v-flex>
|
||||
<v-flex xs4 md1 pa-2>
|
||||
<v-autocomplete
|
||||
v-model="model.Month"
|
||||
:append-icon="model.TakenSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:error="invalidDate"
|
||||
:label="$gettext('Month')"
|
||||
browser-autocomplete="off"
|
||||
hide-details box flat hide-no-data
|
||||
color="secondary-dark"
|
||||
:items="options.MonthsShort()"
|
||||
class="input-month"
|
||||
@change="updateTime">
|
||||
</v-autocomplete>
|
||||
</v-flex>
|
||||
<v-flex xs4 md2 pa-2>
|
||||
<v-autocomplete
|
||||
v-model="model.Year"
|
||||
:append-icon="model.TakenSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:error="invalidDate"
|
||||
:label="$gettext('Year')"
|
||||
browser-autocomplete="off"
|
||||
hide-details box flat hide-no-data
|
||||
color="secondary-dark"
|
||||
:items="options.Years()"
|
||||
class="input-year"
|
||||
@change="updateTime">
|
||||
</v-autocomplete>
|
||||
</v-flex>
|
||||
<v-flex xs4 md1 pa-2>
|
||||
<v-autocomplete
|
||||
v-model="model.Day"
|
||||
:append-icon="model.TakenSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:error="invalidDate"
|
||||
:label="$gettext('Day')"
|
||||
browser-autocomplete="off"
|
||||
hide-details box flat hide-no-data
|
||||
color="secondary-dark"
|
||||
:items="options.Days()"
|
||||
class="input-day"
|
||||
@change="updateTime">
|
||||
</v-autocomplete>
|
||||
</v-flex>
|
||||
<v-flex xs4 md1 pa-2>
|
||||
<v-autocomplete
|
||||
v-model="model.Month"
|
||||
:append-icon="model.TakenSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:error="invalidDate"
|
||||
:label="$gettext('Month')"
|
||||
browser-autocomplete="off"
|
||||
hide-details box flat hide-no-data
|
||||
color="secondary-dark"
|
||||
:items="options.MonthsShort()"
|
||||
class="input-month"
|
||||
@change="updateTime">
|
||||
</v-autocomplete>
|
||||
</v-flex>
|
||||
<v-flex xs4 md2 pa-2>
|
||||
<v-autocomplete
|
||||
v-model="model.Year"
|
||||
:append-icon="model.TakenSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:error="invalidDate"
|
||||
:label="$gettext('Year')"
|
||||
browser-autocomplete="off"
|
||||
hide-details box flat hide-no-data
|
||||
color="secondary-dark"
|
||||
:items="options.Years()"
|
||||
class="input-year"
|
||||
@change="updateTime">
|
||||
</v-autocomplete>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs6 md2 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="time"
|
||||
:append-icon="model.TakenSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:label="model.timeIsUTC() ? $gettext('Time UTC') : $gettext('Local Time')"
|
||||
browser-autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
hide-details box flat
|
||||
return-masked-value mask="##:##:##"
|
||||
color="secondary-dark"
|
||||
class="input-local-time"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
<v-flex xs6 md2 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="time"
|
||||
:append-icon="model.TakenSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:label="model.timeIsUTC() ? $gettext('Time UTC') : $gettext('Local Time')"
|
||||
browser-autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
hide-details box flat
|
||||
return-masked-value mask="##:##:##"
|
||||
color="secondary-dark"
|
||||
class="input-local-time"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs6 sm6 md6 lg3 class="pa-2">
|
||||
<v-autocomplete
|
||||
v-model="model.TimeZone"
|
||||
:disabled="disabled"
|
||||
:label="$gettext('Time Zone')"
|
||||
browser-autocomplete="off"
|
||||
hide-details box flat hide-no-data
|
||||
color="secondary-dark"
|
||||
item-value="ID"
|
||||
item-text="Name"
|
||||
:items="options.TimeZones()"
|
||||
class="input-timezone"
|
||||
@change="updateTime">
|
||||
</v-autocomplete>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm6 md6 lg3 class="pa-2">
|
||||
<v-autocomplete
|
||||
v-model="model.TimeZone"
|
||||
:disabled="disabled"
|
||||
:label="$gettext('Time Zone')"
|
||||
browser-autocomplete="off"
|
||||
hide-details box flat hide-no-data
|
||||
color="secondary-dark"
|
||||
item-value="ID"
|
||||
item-text="Name"
|
||||
:items="options.TimeZones()"
|
||||
class="input-timezone"
|
||||
@change="updateTime">
|
||||
</v-autocomplete>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs12 sm8 md4 lg3 class="pa-2">
|
||||
<v-autocomplete
|
||||
v-model="model.Country"
|
||||
:append-icon="model.PlaceSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:readonly="!!(model.Lat || model.Lng)"
|
||||
:label="$gettext('Country')" hide-details box flat
|
||||
hide-no-data
|
||||
browser-autocomplete="off"
|
||||
color="secondary-dark"
|
||||
item-value="Code"
|
||||
item-text="Name"
|
||||
:items="countries"
|
||||
class="input-country">
|
||||
</v-autocomplete>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm8 md4 lg3 class="pa-2">
|
||||
<v-autocomplete
|
||||
v-model="model.Country"
|
||||
:append-icon="model.PlaceSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:readonly="!!(model.Lat || model.Lng)"
|
||||
:label="$gettext('Country')" hide-details box flat
|
||||
hide-no-data
|
||||
browser-autocomplete="off"
|
||||
color="secondary-dark"
|
||||
item-value="Code"
|
||||
item-text="Name"
|
||||
:items="countries"
|
||||
class="input-country">
|
||||
</v-autocomplete>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs4 md2 lg2 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.Altitude"
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
:label="$gettext('Altitude (m)')"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
class="input-altitude"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
<v-flex xs4 md2 lg2 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.Altitude"
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
:label="$gettext('Altitude (m)')"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
class="input-altitude"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs4 sm6 md3 lg2 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.Lat"
|
||||
:append-icon="model.PlaceSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
:label="$gettext('Latitude')"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
class="input-latitude"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
<v-flex xs4 sm6 md3 lg2 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.Lat"
|
||||
:append-icon="model.PlaceSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
:label="$gettext('Latitude')"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
class="input-latitude"
|
||||
@paste="pastePosition"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs4 sm6 md3 lg2 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.Lng"
|
||||
:append-icon="model.PlaceSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
:label="$gettext('Longitude')"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
class="input-longitude"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
<v-flex xs4 sm6 md3 lg2 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.Lng"
|
||||
:append-icon="model.PlaceSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
:label="$gettext('Longitude')"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
class="input-longitude"
|
||||
@paste="pastePosition"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs12 md6 pa-2 class="p-camera-select">
|
||||
<v-select
|
||||
v-model="model.CameraID"
|
||||
:append-icon="model.CameraSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:label="$gettext('Camera')"
|
||||
:menu-props="{'maxHeight':346}"
|
||||
browser-autocomplete="off"
|
||||
hide-details box flat
|
||||
color="secondary-dark"
|
||||
item-value="ID"
|
||||
item-text="Name"
|
||||
:items="cameraOptions"
|
||||
class="input-camera">
|
||||
</v-select>
|
||||
</v-flex>
|
||||
<v-flex xs12 md6 pa-2 class="p-camera-select">
|
||||
<v-select
|
||||
v-model="model.CameraID"
|
||||
:append-icon="model.CameraSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:label="$gettext('Camera')"
|
||||
:menu-props="{'maxHeight':346}"
|
||||
browser-autocomplete="off"
|
||||
hide-details box flat
|
||||
color="secondary-dark"
|
||||
item-value="ID"
|
||||
item-text="Name"
|
||||
:items="cameraOptions"
|
||||
class="input-camera">
|
||||
</v-select>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs6 md3 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.Iso"
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
label="ISO"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
class="input-iso"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
<v-flex xs6 md3 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.Iso"
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
label="ISO"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
class="input-iso"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs6 md3 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.Exposure"
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
:label="$gettext('Exposure')"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
class="input-exposure"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
<v-flex xs6 md3 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.Exposure"
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
:label="$gettext('Exposure')"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
class="input-exposure"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs12 md6 pa-2 class="p-lens-select">
|
||||
<v-select
|
||||
v-model="model.LensID"
|
||||
:append-icon="model.CameraSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:label="$gettext('Lens')"
|
||||
:menu-props="{'maxHeight':346}"
|
||||
browser-autocomplete="off"
|
||||
hide-details box flat
|
||||
color="secondary-dark"
|
||||
item-value="ID"
|
||||
item-text="Name"
|
||||
:items="lensOptions"
|
||||
class="input-lens">
|
||||
</v-select>
|
||||
</v-flex>
|
||||
<v-flex xs12 md6 pa-2 class="p-lens-select">
|
||||
<v-select
|
||||
v-model="model.LensID"
|
||||
:append-icon="model.CameraSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:label="$gettext('Lens')"
|
||||
:menu-props="{'maxHeight':346}"
|
||||
browser-autocomplete="off"
|
||||
hide-details box flat
|
||||
color="secondary-dark"
|
||||
item-value="ID"
|
||||
item-text="Name"
|
||||
:items="lensOptions"
|
||||
class="input-lens">
|
||||
</v-select>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs6 md3 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.FNumber"f
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
:label="$gettext('F Number')"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
class="input-fnumber"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
<v-flex xs6 md3 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.FNumber" f
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
:label="$gettext('F Number')"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
class="input-fnumber"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs6 md3 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.FocalLength"
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
:label="$gettext('Focal Length')"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
class="input-focal-length"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
<v-flex xs6 md3 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.FocalLength"
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
:label="$gettext('Focal Length')"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
class="input-focal-length"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs12 md6 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.Details.Artist"
|
||||
:append-icon="model.Details.ArtistSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:rules="[textRule]"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
:label="$gettext('Artist')"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
class="input-artist"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
<v-flex xs12 md6 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.Details.Artist"
|
||||
:append-icon="model.Details.ArtistSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:rules="[textRule]"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
:label="$gettext('Artist')"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
class="input-artist"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs6 md3 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.Details.Copyright"
|
||||
:append-icon="model.Details.CopyrightSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:rules="[textRule]"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
:label="$gettext('Copyright')"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
class="input-copyright"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
<v-flex xs6 md3 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="model.Details.Copyright"
|
||||
:append-icon="model.Details.CopyrightSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:rules="[textRule]"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
:label="$gettext('Copyright')"
|
||||
placeholder=""
|
||||
color="secondary-dark"
|
||||
class="input-copyright"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs6 md3 class="pa-2">
|
||||
<v-textarea
|
||||
v-model="model.Details.License"
|
||||
:append-icon="model.Details.LicenseSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:rules="[textRule]"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
auto-grow
|
||||
:label="$gettext('License')"
|
||||
placeholder=""
|
||||
:rows="1"
|
||||
color="secondary-dark"
|
||||
class="input-license"
|
||||
></v-textarea>
|
||||
</v-flex>
|
||||
<v-flex xs6 md3 class="pa-2">
|
||||
<v-textarea
|
||||
v-model="model.Details.License"
|
||||
:append-icon="model.Details.LicenseSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:rules="[textRule]"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
auto-grow
|
||||
:label="$gettext('License')"
|
||||
placeholder=""
|
||||
:rows="1"
|
||||
color="secondary-dark"
|
||||
class="input-license"
|
||||
></v-textarea>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs12 class="pa-2">
|
||||
<v-textarea
|
||||
v-model="model.Details.Subject"
|
||||
:append-icon="model.Details.SubjectSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:rules="[textRule]"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
auto-grow
|
||||
:label="$gettext('Subject')"
|
||||
placeholder=""
|
||||
:rows="1"
|
||||
color="secondary-dark"
|
||||
class="input-subject"
|
||||
></v-textarea>
|
||||
</v-flex>
|
||||
<v-flex xs12 class="pa-2">
|
||||
<v-textarea
|
||||
v-model="model.Details.Subject"
|
||||
:append-icon="model.Details.SubjectSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
:rules="[textRule]"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
auto-grow
|
||||
:label="$gettext('Subject')"
|
||||
placeholder=""
|
||||
:rows="1"
|
||||
color="secondary-dark"
|
||||
class="input-subject"
|
||||
></v-textarea>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs12 class="pa-2">
|
||||
<v-textarea
|
||||
v-model="model.Description"
|
||||
:append-icon="model.DescriptionSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
auto-grow
|
||||
:label="$gettext('Description')"
|
||||
placeholder=""
|
||||
:rows="1"
|
||||
color="secondary-dark"
|
||||
class="input-description"
|
||||
></v-textarea>
|
||||
</v-flex>
|
||||
<v-flex xs12 class="pa-2">
|
||||
<v-textarea
|
||||
v-model="model.Description"
|
||||
:append-icon="model.DescriptionSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
auto-grow
|
||||
:label="$gettext('Description')"
|
||||
placeholder=""
|
||||
:rows="1"
|
||||
color="secondary-dark"
|
||||
class="input-description"
|
||||
></v-textarea>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs12 md8 class="pa-2">
|
||||
<v-textarea
|
||||
v-model="model.Details.Keywords"
|
||||
:append-icon="model.Details.KeywordsSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
auto-grow
|
||||
:label="$gettext('Keywords')"
|
||||
placeholder=""
|
||||
:rows="1"
|
||||
color="secondary-dark"
|
||||
class="input-keywords"
|
||||
></v-textarea>
|
||||
</v-flex>
|
||||
<v-flex xs12 md8 class="pa-2">
|
||||
<v-textarea
|
||||
v-model="model.Details.Keywords"
|
||||
:append-icon="model.Details.KeywordsSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
auto-grow
|
||||
:label="$gettext('Keywords')"
|
||||
placeholder=""
|
||||
:rows="1"
|
||||
color="secondary-dark"
|
||||
class="input-keywords"
|
||||
></v-textarea>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs12 md4 class="pa-2">
|
||||
<v-textarea
|
||||
v-model="model.Details.Notes"
|
||||
:append-icon="model.Details.NotesSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
auto-grow
|
||||
:label="$gettext('Notes')"
|
||||
placeholder=""
|
||||
:rows="1"
|
||||
color="secondary-dark"
|
||||
class="input-notes"
|
||||
></v-textarea>
|
||||
</v-flex>
|
||||
<v-flex xs12 md4 class="pa-2">
|
||||
<v-textarea
|
||||
v-model="model.Details.Notes"
|
||||
:append-icon="model.Details.NotesSrc === 'manual' ? 'check' : ''"
|
||||
:disabled="disabled"
|
||||
hide-details box flat
|
||||
browser-autocomplete="off"
|
||||
auto-grow
|
||||
:label="$gettext('Notes')"
|
||||
placeholder=""
|
||||
:rows="1"
|
||||
color="secondary-dark"
|
||||
class="input-notes"
|
||||
></v-textarea>
|
||||
</v-flex>
|
||||
|
||||
<v-flex v-if="!disabled" xs12 :text-xs-right="!rtl" :text-xs-left="rtl" class="pt-3">
|
||||
<v-btn depressed color="secondary-light" class="compact action-close"
|
||||
@click.stop="close">
|
||||
<translate>Close</translate>
|
||||
</v-btn>
|
||||
<v-btn color="primary-button" depressed dark class="compact action-apply action-approve"
|
||||
@click.stop="save(false)">
|
||||
<span v-if="$config.feature('review') && model.Quality < 3"><translate>Approve</translate></span>
|
||||
<span v-else><translate>Apply</translate></span>
|
||||
</v-btn>
|
||||
<v-btn color="primary-button" depressed dark class="compact action-done hidden-xs-only"
|
||||
@click.stop="save(true)">
|
||||
<translate>Done</translate>
|
||||
</v-btn>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
|
||||
<div class="mt-1 clear"></div>
|
||||
</v-form>
|
||||
</v-container>
|
||||
<v-flex v-if="!disabled" xs12 :text-xs-right="!rtl" :text-xs-left="rtl" class="pt-3">
|
||||
<v-btn depressed color="secondary-light" class="compact action-close"
|
||||
@click.stop="close">
|
||||
<translate>Close</translate>
|
||||
</v-btn>
|
||||
<v-btn color="primary-button" depressed dark class="compact action-apply action-approve"
|
||||
@click.stop="save(false)">
|
||||
<span v-if="$config.feature('review') && model.Quality < 3"><translate>Approve</translate></span>
|
||||
<span v-else><translate>Apply</translate></span>
|
||||
</v-btn>
|
||||
<v-btn color="primary-button" depressed dark class="compact action-done hidden-xs-only"
|
||||
@click.stop="save(true)">
|
||||
<translate>Done</translate>
|
||||
</v-btn>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<div class="mt-1 clear"></div>
|
||||
</v-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -475,6 +471,37 @@ export default {
|
|||
|
||||
this.time = taken.toFormat("HH:mm:ss");
|
||||
},
|
||||
pastePosition(event) {
|
||||
// Auto-fills the lat and lng fields if the text in the clipboard contains two float values.
|
||||
const clipboard = event.clipboardData ? event.clipboardData : window.clipboardData;
|
||||
|
||||
if (!clipboard) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get values from browser clipboard.
|
||||
const text = clipboard.getData("text");
|
||||
|
||||
// Trim spaces before splitting by whitespace and/or commas.
|
||||
const val = text.trim().split(/[ ,]+/);
|
||||
|
||||
// Two values found?
|
||||
if (val.length >= 2) {
|
||||
// Parse values.
|
||||
const lat = parseFloat(val[0]);
|
||||
const lng = parseFloat(val[1]);
|
||||
|
||||
// Lat and long must be valid floating point numbers.
|
||||
if (!isNaN(lat) && lat >= -90 && lat <= 90 &&
|
||||
!isNaN(lng) && lng >= -180 && lng <= 180) {
|
||||
// Update model values.
|
||||
this.model.Lat = lat;
|
||||
this.model.Lng = lng;
|
||||
// Prevent default action.
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
},
|
||||
updateModel() {
|
||||
if (!this.model.hasId()) {
|
||||
return;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<v-expansion-panel-content v-if="!file.Missing" :key="file.UID" class="pa-0 elevation-0 secondary-light"
|
||||
style="margin-top: 1px;">
|
||||
<template #header>
|
||||
<div class="caption">{{ file.baseName(70) }}</div>
|
||||
<div class="caption filename">{{ file.baseName(70) }}</div>
|
||||
</template>
|
||||
<v-card>
|
||||
<v-card-text class="white pa-0">
|
||||
|
@ -66,31 +66,36 @@
|
|||
@click.stop.prevent="showDeleteDialog(file)">
|
||||
<translate>Delete</translate>
|
||||
</v-btn>
|
||||
<v-btn v-if="experimental && canAccessPrivate && file.Primary" small depressed dark color="primary-button"
|
||||
class="btn-action action-open-folder"
|
||||
:href="folderUrl(file)" target="_blank">
|
||||
<translate>File Browser</translate>
|
||||
</v-btn>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td title="Unique ID">
|
||||
UID
|
||||
</td>
|
||||
<td>{{ file.UID | uppercase }}</td>
|
||||
<td><span class="clickable" @click.stop.prevent="copyText(file.UID)">{{ file.UID | uppercase }}</span></td>
|
||||
</tr>
|
||||
<tr v-if="file.InstanceID" title="XMP">
|
||||
<td>
|
||||
<translate>Instance ID</translate>
|
||||
</td>
|
||||
<td>{{ file.InstanceID | uppercase }}</td>
|
||||
<td><span class="clickable" @click.stop.prevent="copyText(file.InstanceID)">{{ file.InstanceID | uppercase }}</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td title="SHA-1">
|
||||
<translate>Hash</translate>
|
||||
</td>
|
||||
<td>{{ file.Hash }}</td>
|
||||
<td><span class="clickable" @click.stop.prevent="copyText(file.Hash)">{{ file.Hash }}</span></td>
|
||||
</tr>
|
||||
<tr v-if="file.Name">
|
||||
<td>
|
||||
<translate>Filename</translate>
|
||||
</td>
|
||||
<td>{{ file.Name }}</td>
|
||||
<td><span class="clickable" @click.stop.prevent="copyText(file.Name)">{{ file.Name }}</span></td>
|
||||
</tr>
|
||||
<tr v-if="file.Root">
|
||||
<td>
|
||||
|
@ -102,7 +107,7 @@
|
|||
<td>
|
||||
<translate>Original Name</translate>
|
||||
</td>
|
||||
<td>{{ file.OriginalName }}</td>
|
||||
<td><span class="clickable" @click.stop.prevent="copyText(file.OriginalName)">{{ file.OriginalName }}</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
@ -202,7 +207,7 @@
|
|||
hide-details
|
||||
color="secondary-dark"
|
||||
:items="options.Orientations()"
|
||||
:readonly="readonly || !features.edit || (file.FileType !== 'jpg' && file.FileType !== 'png') || file.Error"
|
||||
:readonly="readonly || !features.edit || file.Error || (file.Frames && file.Frames > 1) || (file.Duration && file.Duration > 1) || (file.FileType !== 'jpg' && file.FileType !== 'png')"
|
||||
:disabled="busy"
|
||||
class="input-orientation"
|
||||
@change="changeOrientation(file)">
|
||||
|
@ -289,7 +294,10 @@ export default {
|
|||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
uid: String,
|
||||
uid: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -301,8 +309,11 @@ export default {
|
|||
features: this.$config.settings().features,
|
||||
config: this.$config.values,
|
||||
readonly: this.$config.get("readonly"),
|
||||
experimental: this.$config.get("experimental"),
|
||||
canAccessPrivate: this.$config.allow("photos", "access_library") && this.$config.allow("photos", "access_private"),
|
||||
options: options,
|
||||
busy: false,
|
||||
rtl: this.$rtl,
|
||||
listColumns: [
|
||||
{
|
||||
text: this.$gettext('Primary'),
|
||||
|
@ -321,6 +332,18 @@ export default {
|
|||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
async copyText(text) {
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await Util.copyToMachineClipboard(text);
|
||||
this.$notify.success(this.$gettext("Copied to clipboard"));
|
||||
} catch (error) {
|
||||
this.$notify.error(this.$gettext("Failed copying to clipboard"));
|
||||
}
|
||||
},
|
||||
orientationClass(file) {
|
||||
if (!file) {
|
||||
return [];
|
||||
|
@ -351,6 +374,19 @@ export default {
|
|||
openFile(file) {
|
||||
this.$viewer.show([Thumb.fromFile(this.model, file)], 0);
|
||||
},
|
||||
folderUrl(m) {
|
||||
if (!m) {
|
||||
return '#';
|
||||
}
|
||||
|
||||
const name = m.Name;
|
||||
|
||||
// "#" chars in path names must be explicitly escaped,
|
||||
// see https://github.com/photoprism/photoprism/issues/3695
|
||||
const path = name.substring(0, name.lastIndexOf('/'))
|
||||
.replaceAll(":", "%3A").replaceAll("#", "%23");
|
||||
return this.$router.resolve({ path: '/index/files/' + path }).href;
|
||||
},
|
||||
downloadFile(file) {
|
||||
Notify.success(this.$gettext("Downloading…"));
|
||||
|
||||
|
|
|
@ -1,23 +1,24 @@
|
|||
<template>
|
||||
<div class="p-tab p-tab-photo-advanced">
|
||||
<div class="v-table__overflow">
|
||||
<table class="v-datatable v-table theme--light">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>UID</td>
|
||||
<td>{{ model.UID | uppercase }}</td>
|
||||
</tr>
|
||||
<tr v-if="model.DocumentID">
|
||||
<td>Document ID</td>
|
||||
<td>{{ model.DocumentID | uppercase }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td :title="model.TypeSrc">
|
||||
<translate>Type</translate>
|
||||
<v-icon v-if="model.TypeSrc === 'manual'" class="src">check</v-icon>
|
||||
</td>
|
||||
<td>
|
||||
<v-select
|
||||
<v-form ref="form" lazy-validation dense accept-charset="UTF-8" @submit.prevent>
|
||||
<div class="v-table__overflow">
|
||||
<table class="v-datatable v-table theme--light">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>UID</td>
|
||||
<td><span class="clickable" @click.stop.prevent="copyText(model.UID)">{{ model.UID | uppercase }}</span></td>
|
||||
</tr>
|
||||
<tr v-if="model.DocumentID">
|
||||
<td>Document ID</td>
|
||||
<td><span class="clickable" @click.stop.prevent="copyText(model.DocumentID)">{{ model.DocumentID | uppercase }}</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td :title="model.TypeSrc">
|
||||
<translate>Type</translate>
|
||||
<v-icon v-if="model.TypeSrc === 'manual'" class="src">check</v-icon>
|
||||
</td>
|
||||
<td>
|
||||
<v-select
|
||||
v-model="model.Type"
|
||||
flat solo
|
||||
browser-autocomplete="off"
|
||||
|
@ -26,27 +27,27 @@
|
|||
:items="options.PhotoTypes()"
|
||||
class="input-type"
|
||||
@change="save">
|
||||
</v-select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="model.Path">
|
||||
<td>
|
||||
<translate>Folder</translate>
|
||||
</td>
|
||||
<td>{{ model.Path }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<translate>Name</translate>
|
||||
</td>
|
||||
<td>{{ model.Name }}</td>
|
||||
</tr>
|
||||
<tr v-if="model.OriginalName">
|
||||
<td>
|
||||
<translate>Original Name</translate>
|
||||
</td>
|
||||
<td>
|
||||
<v-text-field
|
||||
</v-select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="model.Path">
|
||||
<td>
|
||||
<translate>Folder</translate>
|
||||
</td>
|
||||
<td><span class="clickable" @click.stop.prevent="copyText(model.Path)">{{ model.Path }}</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<translate>Name</translate>
|
||||
</td>
|
||||
<td><span class="clickable" @click.stop.prevent="copyText(model.Name)">{{ model.Name }}</span></td>
|
||||
</tr>
|
||||
<tr v-if="model.OriginalName">
|
||||
<td>
|
||||
<translate>Original Name</translate>
|
||||
</td>
|
||||
<td>
|
||||
<v-text-field
|
||||
v-model="model.OriginalName"
|
||||
flat solo dense hide-details
|
||||
browser-autocomplete="off"
|
||||
|
@ -54,56 +55,71 @@
|
|||
autocapitalize="none"
|
||||
color="secondary-dark"
|
||||
@change="save"
|
||||
></v-text-field>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td :title="model.TitleSrc">
|
||||
<translate>Title</translate>
|
||||
<v-icon v-if="model.TitleSrc === 'manual'" class="src">check</v-icon>
|
||||
</td>
|
||||
<td>{{ model.Title }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td :title="model.TakenSrc">
|
||||
<translate>Taken</translate>
|
||||
<v-icon v-if="model.TakenSrc === 'manual'" class="src">check</v-icon>
|
||||
</td>
|
||||
<td>{{ model.getDateString() }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<translate>Quality Score</translate>
|
||||
</td>
|
||||
<td>
|
||||
<v-rating v-model="model.Quality" :length="7" readonly small></v-rating>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<translate>Resolution</translate>
|
||||
</td>
|
||||
<td>{{ model.Resolution }} MP</td>
|
||||
</tr>
|
||||
<tr v-if="model.Faces > 0">
|
||||
<td>
|
||||
<translate>Faces</translate>
|
||||
</td>
|
||||
<td>{{ model.Faces }}</td>
|
||||
</tr>
|
||||
<tr v-if="model.CameraSerial">
|
||||
<td>
|
||||
<translate>Camera Serial</translate>
|
||||
</td>
|
||||
<td>{{ model.CameraSerial }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="model.Stack < 1">
|
||||
<td>
|
||||
<translate>Stackable</translate>
|
||||
</td>
|
||||
<td>
|
||||
<v-switch
|
||||
></v-text-field>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td :title="sourceName(model.TitleSrc)">
|
||||
<translate>Title</translate>
|
||||
<v-icon v-if="model.TitleSrc === 'manual'" class="src">check</v-icon>
|
||||
</td>
|
||||
<td :title="sourceName(model.TitleSrc)">
|
||||
<span class="clickable" @click.stop.prevent="copyText(model.Title)">{{ model.Title }}</span>
|
||||
<v-icon v-if="model.TitleSrc === 'name'" class="src">insert_drive_file</v-icon>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td :title="sourceName(model.TakenSrc)">
|
||||
<translate>Taken</translate>
|
||||
<v-icon v-if="model.TakenSrc === 'manual'" class="src">check</v-icon>
|
||||
</td>
|
||||
<td :title="sourceName(model.TakenSrc)">
|
||||
{{ model.getDateString() }}
|
||||
<v-icon v-if="model.TakenSrc === 'name' || model.TakenSrc === 'estimate'" class="src">insights</v-icon>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="albums.length > 0">
|
||||
<td>
|
||||
<translate>Albums</translate>
|
||||
</td>
|
||||
<td>
|
||||
<a v-for="(a, i) in albums" :key="i" :href="a.url" class="primary--text text-link"
|
||||
target="_blank"><span v-if="i > 0">, </span>{{ a.title }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<translate>Quality Score</translate>
|
||||
</td>
|
||||
<td>
|
||||
<v-rating v-model="model.Quality" :length="7" readonly small></v-rating>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<translate>Resolution</translate>
|
||||
</td>
|
||||
<td>{{ model.Resolution }} MP</td>
|
||||
</tr>
|
||||
<tr v-if="model.Faces > 0">
|
||||
<td>
|
||||
<translate>Faces</translate>
|
||||
</td>
|
||||
<td>{{ model.Faces }}</td>
|
||||
</tr>
|
||||
<tr v-if="model.CameraSerial">
|
||||
<td>
|
||||
<translate>Camera Serial</translate>
|
||||
</td>
|
||||
<td>{{ model.CameraSerial }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="model.Stack < 1">
|
||||
<td>
|
||||
<translate>Stackable</translate>
|
||||
</td>
|
||||
<td>
|
||||
<v-switch
|
||||
v-model="model.Stack"
|
||||
hide-details
|
||||
class="input-stackable"
|
||||
|
@ -111,104 +127,105 @@
|
|||
:false-value="-1"
|
||||
:label="model.Stack > - 1 ? $gettext('Yes') : $gettext('No')"
|
||||
@change="save"
|
||||
></v-switch>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<translate>Favorite</translate>
|
||||
</td>
|
||||
<td>
|
||||
<v-switch
|
||||
></v-switch>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<translate>Favorite</translate>
|
||||
</td>
|
||||
<td>
|
||||
<v-switch
|
||||
v-model="model.Favorite"
|
||||
hide-details
|
||||
class="input-favorite"
|
||||
:label="model.Favorite ? $gettext('Yes') : $gettext('No')"
|
||||
@change="save"
|
||||
></v-switch>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="$config.feature('private')">
|
||||
<td>
|
||||
<translate>Private</translate>
|
||||
</td>
|
||||
<td>
|
||||
<v-switch
|
||||
></v-switch>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="$config.feature('private')">
|
||||
<td>
|
||||
<translate>Private</translate>
|
||||
</td>
|
||||
<td>
|
||||
<v-switch
|
||||
v-model="model.Private"
|
||||
hide-details
|
||||
class="input-private"
|
||||
:label="model.Private ? $gettext('Yes') : $gettext('No')"
|
||||
@change="save"
|
||||
></v-switch>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<translate>Scan</translate>
|
||||
</td>
|
||||
<td>
|
||||
<v-switch
|
||||
></v-switch>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<translate>Scan</translate>
|
||||
</td>
|
||||
<td>
|
||||
<v-switch
|
||||
v-model="model.Scan"
|
||||
hide-details
|
||||
class="input-scan"
|
||||
:label="model.Scan ? $gettext('Yes') : $gettext('No')"
|
||||
@change="save"
|
||||
></v-switch>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<translate>Panorama</translate>
|
||||
</td>
|
||||
<td>
|
||||
<v-switch
|
||||
></v-switch>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<translate>Panorama</translate>
|
||||
</td>
|
||||
<td>
|
||||
<v-switch
|
||||
v-model="model.Panorama"
|
||||
hide-details
|
||||
class="input-panorama"
|
||||
:label="model.Panorama ? $gettext('Yes') : $gettext('No')"
|
||||
@change="save"
|
||||
></v-switch>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td :title="model.PlaceSrc">
|
||||
<translate>Place</translate>
|
||||
<v-icon v-if="model.PlaceSrc === 'manual'" class="src">check</v-icon>
|
||||
</td>
|
||||
<td>
|
||||
{{ model.locationInfo() }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="model.Lat">
|
||||
<td>
|
||||
<translate>Latitude</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ model.Lat }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="model.Lng">
|
||||
<td>
|
||||
<translate>Longitude</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ model.Lng }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="model.Altitude">
|
||||
<td>
|
||||
<translate>Altitude</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ model.Altitude }} m
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="model.Lat">
|
||||
<td>
|
||||
<translate>Accuracy</translate>
|
||||
</td>
|
||||
<td>
|
||||
<v-text-field
|
||||
></v-switch>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td :title="sourceName(model.PlaceSrc)">
|
||||
<translate>Place</translate>
|
||||
<v-icon v-if="model.PlaceSrc === 'manual'" class="src">check</v-icon>
|
||||
</td>
|
||||
<td :title="sourceName(model.PlaceSrc)">
|
||||
{{ model.locationInfo() }}
|
||||
<v-icon v-if="model.PlaceSrc === 'estimate'" class="src">insights</v-icon>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="model.Lat">
|
||||
<td>
|
||||
<translate>Latitude</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ model.Lat }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="model.Lng">
|
||||
<td>
|
||||
<translate>Longitude</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ model.Lng }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="model.Altitude">
|
||||
<td>
|
||||
<translate>Altitude</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ model.Altitude }} m
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="model.Lat">
|
||||
<td>
|
||||
<translate>Accuracy</translate>
|
||||
</td>
|
||||
<td>
|
||||
<v-text-field
|
||||
v-model="model.CellAccuracy"
|
||||
flat solo dense hide-details
|
||||
browser-autocomplete="off"
|
||||
|
@ -219,52 +236,53 @@
|
|||
suffix="m"
|
||||
style="width: 100px;"
|
||||
@change="save"
|
||||
></v-text-field>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<translate>Created</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ formatTime(model.CreatedAt) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<translate>Updated</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ formatTime(model.UpdatedAt) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="model.EditedAt">
|
||||
<td>
|
||||
<translate>Edited</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ formatTime(model.EditedAt) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="model.CheckedAt">
|
||||
<td>
|
||||
<translate>Checked</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ formatTime(model.CheckedAt) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="model.DeletedAt">
|
||||
<td>
|
||||
<translate>Archived</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ formatTime(model.DeletedAt) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
></v-text-field>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<translate>Created</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ formatTime(model.CreatedAt) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<translate>Updated</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ formatTime(model.UpdatedAt) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="model.EditedAt">
|
||||
<td>
|
||||
<translate>Edited</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ formatTime(model.EditedAt) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="model.CheckedAt">
|
||||
<td>
|
||||
<translate>Checked</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ formatTime(model.CheckedAt) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="model.DeletedAt">
|
||||
<td>
|
||||
<translate>Archived</translate>
|
||||
</td>
|
||||
<td>
|
||||
{{ formatTime(model.DeletedAt) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</v-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -272,15 +290,21 @@
|
|||
import Thumb from "model/thumb";
|
||||
import {DateTime, Info} from "luxon";
|
||||
import * as options from "options/options";
|
||||
import {T} from "common/vm";
|
||||
import Util from "common/util";
|
||||
|
||||
export default {
|
||||
name: 'PTabPhotoAdvanced',
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
default: () => {
|
||||
},
|
||||
},
|
||||
uid: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
uid: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -303,8 +327,53 @@ export default {
|
|||
|
||||
return result;
|
||||
},
|
||||
albums() {
|
||||
if (!this.model || !this.model.Albums || this.model.Albums.length < 1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const results = [];
|
||||
|
||||
this.model.Albums.forEach(a => results.push({"title": a.Title, "url": this.albumUrl(a)}));
|
||||
|
||||
return results;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async copyText(text) {
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await Util.copyToMachineClipboard(text);
|
||||
this.$notify.success(this.$gettext("Copied to clipboard"));
|
||||
} catch (error) {
|
||||
this.$notify.error(this.$gettext("Failed copying to clipboard"));
|
||||
}
|
||||
},
|
||||
sourceName(s) {
|
||||
switch (s) {
|
||||
case "":
|
||||
case "auto":
|
||||
case "manual":
|
||||
return "";
|
||||
case "meta":
|
||||
return this.$gettext('Metadata');
|
||||
case "xmp":
|
||||
return "XMP";
|
||||
case "estimate":
|
||||
return this.$gettext("Estimate");
|
||||
case "name":
|
||||
return this.$gettext("Name");
|
||||
case "image":
|
||||
return this.$gettext("Image");
|
||||
case "location":
|
||||
return this.$gettext("Location");
|
||||
default:
|
||||
return T(Util.capitalize(s));
|
||||
}
|
||||
},
|
||||
formatTime(s) {
|
||||
return DateTime.fromISO(s).toLocaleString(DateTime.DATETIME_MED);
|
||||
},
|
||||
|
@ -317,6 +386,13 @@ export default {
|
|||
openPhoto() {
|
||||
this.$viewer.show(Thumb.fromFiles([this.model]), 0);
|
||||
},
|
||||
albumUrl(m) {
|
||||
if (!m) {
|
||||
return '#';
|
||||
}
|
||||
|
||||
return this.$router.resolve({name: 'album', params: {uid: m.UID, slug: 'view'}}).href;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,102 +1,117 @@
|
|||
<template>
|
||||
<div class="p-tab p-tab-photo-labels">
|
||||
<v-data-table
|
||||
v-model="selected"
|
||||
:headers="listColumns"
|
||||
:items="model.Labels"
|
||||
hide-actions
|
||||
class="elevation-0 p-files p-files-list p-results"
|
||||
disable-initial-sort
|
||||
item-key="ID"
|
||||
:no-data-text="$gettext('No labels found')"
|
||||
>
|
||||
<template #items="props" class="p-file">
|
||||
<td>
|
||||
<v-edit-dialog
|
||||
:return-value.sync="props.item.Label.Name"
|
||||
lazy
|
||||
class="p-inline-edit"
|
||||
@save="renameLabel(props.item.Label)"
|
||||
<v-form ref="form" lazy-validation dense accept-charset="UTF-8" @submit.prevent>
|
||||
<v-layout class="pa-2-md-and-up" row wrap align-top fill-height>
|
||||
<v-flex class="pa-2 hidden-sm-and-down" xs12 md2 xxl1 fill-height>
|
||||
<p-photo-preview :model="model"></p-photo-preview>
|
||||
</v-flex>
|
||||
<v-flex class="pa-2-md-and-up ra-4-table-md-and-up" xs12 md10 xxl11 fill-width fill-height>
|
||||
<v-data-table
|
||||
v-model="selected"
|
||||
:headers="listColumns"
|
||||
:items="model.Labels"
|
||||
hide-actions
|
||||
class="elevation-0 p-results"
|
||||
disable-initial-sort
|
||||
item-key="ID"
|
||||
:no-data-text="$gettext('No labels found')"
|
||||
>
|
||||
{{ props.item.Label.Name }}
|
||||
<template #input>
|
||||
<v-text-field
|
||||
v-model="props.item.Label.Name"
|
||||
:rules="[nameRule]"
|
||||
:label="$gettext('Name')"
|
||||
color="secondary-dark"
|
||||
class="input-rename background-inherit elevation-0"
|
||||
single-line autofocus solo hide-details
|
||||
></v-text-field>
|
||||
<template #items="props" class="p-file">
|
||||
<td>
|
||||
<v-edit-dialog
|
||||
:return-value.sync="props.item.Label.Name"
|
||||
lazy
|
||||
class="p-inline-edit"
|
||||
@save="renameLabel(props.item.Label)"
|
||||
>
|
||||
{{ props.item.Label.Name }}
|
||||
<template #input>
|
||||
<v-text-field
|
||||
v-model="props.item.Label.Name"
|
||||
:rules="[nameRule]"
|
||||
:label="$gettext('Name')"
|
||||
color="secondary-dark"
|
||||
class="input-rename background-inherit elevation-0"
|
||||
single-line autofocus solo hide-details
|
||||
></v-text-field>
|
||||
</template>
|
||||
</v-edit-dialog>
|
||||
</td>
|
||||
<td class="text-xs-left">{{ sourceName(props.item.LabelSrc) }}</td>
|
||||
<td class="text-xs-center">{{ 100 - props.item.Uncertainty }}%</td>
|
||||
<td class="text-xs-center">
|
||||
<v-btn v-if="disabled" icon small flat :ripple="false"
|
||||
class="action-view" title="Search"
|
||||
@click.stop.prevent="searchLabel(props.item.Label)">
|
||||
<v-icon color="secondary-dark">search</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="props.item.Uncertainty < 100 && props.item.LabelSrc === 'manual'" icon
|
||||
small flat :ripple="false"
|
||||
class="action-delete" title="Delete"
|
||||
@click.stop.prevent="removeLabel(props.item.Label)">
|
||||
<v-icon color="secondary-dark">delete</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="props.item.Uncertainty < 100" icon small flat :ripple="false"
|
||||
class="action-remove" title="Remove"
|
||||
@click.stop.prevent="removeLabel(props.item.Label)">
|
||||
<v-icon color="secondary-dark">remove</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else icon small flat :ripple="false"
|
||||
class="action-on" title="Activate"
|
||||
@click.stop.prevent="activateLabel(props.item.Label)">
|
||||
<v-icon color="secondary-dark">add</v-icon>
|
||||
</v-btn>
|
||||
</td>
|
||||
</template>
|
||||
</v-edit-dialog>
|
||||
</td>
|
||||
<td class="text-xs-left">{{ sourceName(props.item.LabelSrc) }}</td>
|
||||
<td class="text-xs-center">{{ 100 - props.item.Uncertainty }}%</td>
|
||||
<td class="text-xs-center">
|
||||
<v-btn v-if="disabled" icon small flat :ripple="false"
|
||||
class="action-view" title="Search"
|
||||
@click.stop.prevent="searchLabel(props.item.Label)">
|
||||
<v-icon color="secondary-dark">search</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="props.item.Uncertainty < 100 && props.item.LabelSrc === 'manual'" icon
|
||||
small flat :ripple="false"
|
||||
class="action-delete" title="Delete"
|
||||
@click.stop.prevent="removeLabel(props.item.Label)">
|
||||
<v-icon color="secondary-dark">delete</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="props.item.Uncertainty < 100" icon small flat :ripple="false"
|
||||
class="action-remove" title="Remove"
|
||||
@click.stop.prevent="removeLabel(props.item.Label)">
|
||||
<v-icon color="secondary-dark">remove</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else icon small flat :ripple="false"
|
||||
class="action-on" title="Activate"
|
||||
@click.stop.prevent="activateLabel(props.item.Label)">
|
||||
<v-icon color="secondary-dark">add</v-icon>
|
||||
</v-btn>
|
||||
</td>
|
||||
</template>
|
||||
<template v-if="!disabled" #footer>
|
||||
<td>
|
||||
<v-text-field
|
||||
v-model="newLabel"
|
||||
:rules="[nameRule]"
|
||||
color="secondary-dark"
|
||||
browser-autocomplete="off"
|
||||
:label="$gettext('Name')"
|
||||
single-line
|
||||
flat solo hide-details
|
||||
autofocus
|
||||
class="input-label"
|
||||
@keyup.enter.native="addLabel"
|
||||
></v-text-field>
|
||||
</td>
|
||||
<td class="text-xs-left">{{ sourceName('manual') }}</td>
|
||||
<td class="text-xs-center">100%</td>
|
||||
<td class="text-xs-center">
|
||||
<v-btn icon small flat :ripple="false" title="Add"
|
||||
class="p-photo-label-add"
|
||||
@click.stop.prevent="addLabel">
|
||||
<v-icon color="secondary-dark">add</v-icon>
|
||||
</v-btn>
|
||||
</td>
|
||||
</template>
|
||||
</v-data-table>
|
||||
<template v-if="!disabled" #footer>
|
||||
<td>
|
||||
<v-text-field
|
||||
v-model="newLabel"
|
||||
:rules="[nameRule]"
|
||||
color="secondary-dark"
|
||||
browser-autocomplete="off"
|
||||
:label="$gettext('Name')"
|
||||
single-line
|
||||
flat solo hide-details
|
||||
autofocus
|
||||
class="input-label"
|
||||
@keyup.enter.native="addLabel"
|
||||
></v-text-field>
|
||||
</td>
|
||||
<td class="text-xs-left">{{ sourceName('manual') }}</td>
|
||||
<td class="text-xs-center">100%</td>
|
||||
<td class="text-xs-center">
|
||||
<v-btn icon small flat :ripple="false" title="Add"
|
||||
class="p-photo-label-add"
|
||||
@click.stop.prevent="addLabel">
|
||||
<v-icon color="secondary-dark">add</v-icon>
|
||||
</v-btn>
|
||||
</td>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<!-- div class="mt-1 clear"></div -->
|
||||
</v-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Label from "model/label";
|
||||
import Thumb from "model/thumb";
|
||||
|
||||
export default {
|
||||
name: 'PTabPhotoLabels',
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
default: () => {
|
||||
},
|
||||
},
|
||||
uid: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
uid: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -170,6 +185,9 @@ export default {
|
|||
this.$router.push({name: 'all', query: {q: 'label:' + label.Slug}}).catch(() => {});
|
||||
this.$emit('close');
|
||||
},
|
||||
openPhoto() {
|
||||
this.$viewer.show(Thumb.fromFiles([this.model]), 0);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<translate>Recognition starts after indexing has been completed.</translate>
|
||||
</p>
|
||||
</v-alert>
|
||||
<v-layout row wrap class="search-results face-results cards-view">
|
||||
<v-layout class="search-results face-results cards-view" row wrap fill-height>
|
||||
<v-flex
|
||||
v-for="(marker, index) in markers"
|
||||
:key="index"
|
||||
|
|
|
@ -408,7 +408,7 @@ export default {
|
|||
for (let i = 0; i < thumbs.length; i++) {
|
||||
let t = thumbs[i];
|
||||
|
||||
result.push({"text": t.w + 'x' + t.h, "value": t.size});
|
||||
result.push({"text": t.w + ' × ' + t.h, "value": t.size});
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -62,12 +62,16 @@
|
|||
<p class="px-2 ma-0 text-xs-right opacity-85"><span v-if="eta">{{ eta }}</span></p>
|
||||
</v-progress-linear>
|
||||
|
||||
<p v-if="safe" class="body-1">
|
||||
<p v-if="isDemo" class="body-2">
|
||||
<translate :translate-params="{n: fileLimit}">You can upload up to %{n} files for test purposes.</translate>
|
||||
<translate>Please do not upload any private, unlawful or offensive pictures. </translate>
|
||||
</p>
|
||||
<p v-else-if="rejectNSFW" class="body-2">
|
||||
<translate>Please don't upload photos containing offensive content.</translate>
|
||||
<translate>Uploads that may contain such images will be rejected automatically.</translate>
|
||||
</p>
|
||||
|
||||
<p v-if="review" class="body-1">
|
||||
<p v-if="featReview" class="body-1">
|
||||
<translate>Non-photographic and low-quality images require a review before they appear in search results.</translate>
|
||||
</p>
|
||||
|
||||
|
@ -99,8 +103,13 @@ export default {
|
|||
name: 'PUploadDialog',
|
||||
props: {
|
||||
show: Boolean,
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
const isDemo = this.$config.get("demo");
|
||||
return {
|
||||
albums: [],
|
||||
selectedAlbums: [],
|
||||
|
@ -120,16 +129,29 @@ export default {
|
|||
remainingTime: -1,
|
||||
eta: "",
|
||||
token: "",
|
||||
review: this.$config.feature("review"),
|
||||
safe: !this.$config.get("uploadNSFW"),
|
||||
isDemo: isDemo,
|
||||
fileLimit: isDemo ? 3 : 0,
|
||||
rejectNSFW: !this.$config.get("uploadNSFW"),
|
||||
featReview: this.$config.feature("review"),
|
||||
rtl: this.$rtl,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
show: function () {
|
||||
this.reset();
|
||||
this.review = this.$config.feature("review");
|
||||
this.safe = !this.$config.get("uploadNSFW");
|
||||
this.isDemo = this.$config.get("demo");
|
||||
this.fileLimit = this.isDemo ? 3 : 0;
|
||||
this.rejectNSFW = !this.$config.get("uploadNSFW");
|
||||
this.featReview = this.$config.feature("review");
|
||||
|
||||
// Set currently selected albums.
|
||||
if (this.data && Array.isArray(this.data.albums)) {
|
||||
this.selectedAlbums = this.data.albums;
|
||||
} else {
|
||||
this.selectedAlbums = [];
|
||||
}
|
||||
|
||||
// Fetch albums from backend.
|
||||
this.findAlbums("");
|
||||
}
|
||||
},
|
||||
|
@ -180,8 +202,8 @@ export default {
|
|||
this.failed = false;
|
||||
this.current = 0;
|
||||
this.total = 0;
|
||||
this.totalFailed = 0;
|
||||
this.totalSize = 0;
|
||||
this.totalFailed = 0;
|
||||
this.completedSize = 0;
|
||||
this.completedTotal = 0;
|
||||
this.started = 0;
|
||||
|
@ -233,10 +255,19 @@ export default {
|
|||
return;
|
||||
}
|
||||
|
||||
this.selected = this.$refs.upload.files;
|
||||
this.total = this.selected.length;
|
||||
const files = this.$refs.upload.files;
|
||||
|
||||
if (this.total < 1) {
|
||||
// Too many files selected for upload?
|
||||
if (this.isDemo && files && files.length > this.fileLimit) {
|
||||
Notify.error(this.$gettext("Too many files selected"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = files;
|
||||
this.total = files.length;
|
||||
|
||||
// No files selected?
|
||||
if (!this.selected || this.total < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue