File Types: Add experimental support for animated GIFs #590 #2207

Animated GIFs are transcoded to AVC because it is much smaller and
thus also suitable for long/large animations. In addition, this commit
adds support for more metadata fields such as frame rate, number of
frames, file capture timestamp (unix milliseconds), media type,
and software version. Support for SVG files can later be implemented in
a similar way.
This commit is contained in:
Michael Mayer 2022-04-13 22:17:59 +02:00
parent 4253045b14
commit 82d61d1f93
165 changed files with 1569 additions and 768 deletions

View file

@ -1,5 +1,5 @@
# Debian 12, Codename "Bookworm"
FROM photoprism/develop:220405-bookworm
FROM photoprism/develop:220413-bookworm
## alternative base images
# FROM photoprism/develop:bullseye # Debian 11, Codename "Bullseye"

View file

@ -623,9 +623,9 @@ copy of the Program in return for a fee.
(a) PhotoPrisms Brand Assets — including trademarks, logos, icons, fonts,
corporate design, product and service names, and any other brand features
and elements, whether registered or unregistered („Brand Assets“) — are
proprietary assets owned exclusively by Michael Mayer and his legal successors
(„PhotoPrism“). We reserve the right to object to any use or misuse in any
jurisdiction worldwide. Visit <https://photoprism.app/trademark> to learn more.
proprietary assets owned exclusively by PhotoPrism UG („PhotoPrism“). We
reserve the right to object to any use or misuse in any jurisdiction
worldwide. Visit <https://photoprism.app/trademark> to learn more.
(b) Contributors, licensees, business partners, and other third parties
may never claim ownership of PhotoPrism's Brand Assets or brands confusingly

View file

@ -4,64 +4,72 @@
TERMS OF USE
(a) We hereby grant you a non-exclusive, non-transferable right to use the
Digital Assets accompanying the software, embedded in the documentation,
or provided for download — such as icons, fonts, illustrations, graphics,
Digital Assets accompanying the software, embedded in the documentation, or
provided for download — such as icons, fonts, illustrations, graphics,
wallpapers, videos, sounds, models, and sample files („Digital Assets“) —
as part of the Official Software Distribution, unless otherwise noted.
as part of the Software distributed by us, unless otherwise noted.
(b) Because some Digital Assets are licensed to us solely for direct
distribution, we cannot redistribute them under a more permissive license
for other purposes. If the author or copyright holder has not released them
under a permissive license, you must obtain a license before using them in
your own work, whether commercial or non-commercial in nature.
distribution, we cannot redistribute them under a more permissive license for
other purposes. If the author or copyright holder has not released them under a
permissive license, you must obtain a license before using them in your own
work, whether commercial or non-commercial in nature.
(c) Related documentation is available under the terms of the CC BY-NC-SA
4.0 License, see <https://docs.photoprism.app/license/docs>. Other terms may
apply to Digital Assets — in particular illustrations, graphics, and videos
— embedded in the documentation if they are licensed to us solely for direct
distribution. When in doubt, please ask before distributing or using
them for other works.
(c) Related documentation is available under the terms of the CC BY-NC-SA 4.0
License. Other terms may apply to Digital Assets — in particular
illustrations, graphics, and videos — embedded in the documentation if they
are licensed to us solely for direct distribution. When in doubt, please ask
before distributing or using them for other works.
TRADEMARK AND BRAND ASSETS
(a) PhotoPrisms Brand Assets — including trademarks, logos, icons, fonts,
corporate design, product and service names, and any other brand features
and elements, whether registered or unregistered („Brand Assets“) — are
proprietary assets owned exclusively by Michael Mayer and his legal successors
(„PhotoPrism“). We reserve the right to object to any use or misuse in any
jurisdiction worldwide. Visit <https://photoprism.app/trademark> to learn more.
corporate design, product and service names, and any other brand features and
elements, whether registered or unregistered („Brand Assets“) — are
proprietary assets owned exclusively by PhotoPrism UG („PhotoPrism“). We
reserve the right to object to any use or misuse in any jurisdiction worldwide.
Visit photoprism.app/trademark to learn more.
(b) Contributors, licensees, business partners, and other third parties
may never claim ownership of PhotoPrism's Brand Assets or brands confusingly
similar to PhotoPrism's Brand Assets in any way, including, without
limitation, as a trademark, service mark, company name or designation,
domain name, social media profile/handle, or in any other manner.
(b) Contributors, licensees, business partners, and other third parties may
never claim ownership of PhotoPrism's Brand Assets or brands confusingly
similar to PhotoPrism's Brand Assets in any way, including, without limitation,
as a trademark, service mark, company name or designation, domain name, social
media profile/handle, or in any other manner.
(c) You may not include the PhotoPrism trademark in the name of your app,
product, or service, whether commercial or non-commercial in nature. This
includes online services such as e-commerce, community, blog, information,
advertising, and personal home pages, as well as apps, app stores, client
apps, or third-party apps that interact with PhotoPrism.
advertising, and personal home pages, as well as apps, app stores, client apps,
or third-party apps that interact with PhotoPrism.
(d) In the event that any provision is found to be unenforceable by a court
or other competent jurisdiction, the remaining portions hereof shall remain
in full force and effect.
DISCLAIMER OF WARRANTY AND LIMITATION OF LIABILITY
DISCLAIMER OF WARRANTY
OUR SOFTWARE, SERVICES, DOCUMENTATION, AND DIGITAL ASSETS ARE PROVIDED
“AS-IS” AND WITHOUT WARRANTY OF ANY KIND. WE DISCLAIM ALL WARRANTIES,
EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, TITLE, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE.
LIMITATION OF LIABILITY
WE WILL NOT BE LIABLE FOR ANY DAMAGES ASSOCIATED WITH OUR SOFTWARE AND
SERVICES, INCLUDING WITHOUT LIMITATION ORDINARY, INCIDENTAL, INDIRECT, OR
CONSEQUENTIAL DAMAGES OF ANY KIND, INCLUDING BUT NOT LIMITED TO DAMAGES
RELATING TO LOST DATA OR LOST PROFITS, EVEN IF WE HAVE BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
If the disclaimer of warranty and limitation of liability provided above
cannot be given local legal effect according to their terms, reviewing courts
shall apply local law that most closely approximates an absolute waiver of
all civil liability in connection with our software and services, unless a
warranty or assumption of liability was provided in return for a fee.
SEVERABILITY
(a) If the Disclaimer of Warranty and Limitation of Liability set forth above
cannot be given local legal effect according to their terms, the reviewing
courts shall apply the local law that most closely approximates an absolute
waiver of any civil liability in connection with our software and services,
unless a written warranty or assumption of liability has been granted for a fee.
(b) In the event that any provision set forth above is found by a court of
competent jurisdiction to be invalid, illegal, void or unenforceable, the
remaining provisions shall remain in full force and effect and shall not be
impaired, affected or invalidated in any way.
(c) It is hereby agreed and declared that it is the intention of the parties to
enforce the remaining provisions without the inclusion of those provisions that
may subsequently be declared invalid, illegal, void or unenforceable.

View file

@ -3,65 +3,73 @@
## Terms of Use ##
(a) We hereby grant you a non-exclusive, non-transferable right to use the
Digital Assets accompanying the software, embedded in the documentation,
or provided for download — such as icons, fonts, illustrations, graphics,
Digital Assets accompanying the software, embedded in the documentation, or
provided for download — such as icons, fonts, illustrations, graphics,
wallpapers, videos, sounds, models, and sample files („Digital Assets“) —
as part of the Official Software Distribution, **unless otherwise noted**.
as part of the Software distributed by us, unless otherwise noted.
(b) Because some Digital Assets are licensed to us solely for direct
distribution, we cannot redistribute them under a more permissive license
for other purposes. If the author or copyright holder has not released them
under a permissive license, you must obtain a license before using them in
your own work, whether commercial or non-commercial in nature.
distribution, we cannot redistribute them under a more permissive license for
other purposes. If the author or copyright holder has not released them under a
permissive license, you must obtain a license before using them in your own
work, whether commercial or non-commercial in nature.
(c) Related [documentation](https://docs.photoprism.app/) is available under
the terms of the [CC BY-NC-SA 4.0 License](https://docs.photoprism.app/license/docs/).
Other terms may apply to Digital Assets — in particular illustrations,
graphics, and videos — embedded in the documentation if they are licensed
to us solely for direct distribution. When in doubt, please ask before
distributing or using them for other works.
Other terms may apply to Digital Assets — in particular illustrations, graphics,
and videos — embedded in the documentation if they are licensed to us solely
for direct distribution. When in doubt, please ask before distributing or
using them for other works.
## Trademark and Brand Assets ##
(a) PhotoPrisms Brand Assets — including trademarks, logos, icons, fonts,
corporate design, product and service names, and any other brand features
and elements, whether registered or unregistered („Brand Assets“) — are
proprietary assets owned exclusively by Michael Mayer and his legal successors
(„PhotoPrism“). We reserve the right to object to any use or misuse in any
jurisdiction worldwide. Visit [photoprism.app/trademark](https://photoprism.app/trademark)
to learn more.
corporate design, product and service names, and any other brand features and
elements, whether registered or unregistered („Brand Assets“) — are
proprietary assets owned exclusively by PhotoPrism UG („PhotoPrism“). We
reserve the right to object to any use or misuse in any jurisdiction worldwide.
Visit photoprism.app/trademark to learn more.
(b) Contributors, licensees, business partners, and other third parties
may never claim ownership of PhotoPrism's Brand Assets or brands confusingly
similar to PhotoPrism's Brand Assets in any way, including, without
limitation, as a trademark, service mark, company name or designation,
domain name, social media profile/handle, or in any other manner.
(b) Contributors, licensees, business partners, and other third parties may
never claim ownership of PhotoPrism's Brand Assets or brands confusingly
similar to PhotoPrism's Brand Assets in any way, including, without limitation,
as a trademark, service mark, company name or designation, domain name, social
media profile/handle, or in any other manner.
(c) You may not include the PhotoPrism trademark in the name of your app,
product, or service, whether commercial or non-commercial in nature. This
includes online services such as e-commerce, community, blog, information,
advertising, and personal home pages, as well as apps, app stores, client
apps, or third-party apps that interact with PhotoPrism.
advertising, and personal home pages, as well as apps, app stores, client apps,
or third-party apps that interact with PhotoPrism.
(d) In the event that any provision is found to be unenforceable by a court
or other competent jurisdiction, the remaining portions hereof shall remain
in full force and effect.
## Disclaimer of Warranty and Limitation of Liability ##
## Disclaimer of Warranty ##
OUR SOFTWARE, SERVICES, DOCUMENTATION, AND DIGITAL ASSETS ARE PROVIDED
“AS-IS” AND WITHOUT WARRANTY OF ANY KIND. WE DISCLAIM ALL WARRANTIES,
EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, TITLE, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE.
## Limitation of Liability ##
WE WILL NOT BE LIABLE FOR ANY DAMAGES ASSOCIATED WITH OUR SOFTWARE AND
SERVICES, INCLUDING WITHOUT LIMITATION ORDINARY, INCIDENTAL, INDIRECT, OR
CONSEQUENTIAL DAMAGES OF ANY KIND, INCLUDING BUT NOT LIMITED TO DAMAGES
RELATING TO LOST DATA OR LOST PROFITS, EVEN IF WE HAVE BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
If the disclaimer of warranty and limitation of liability provided above
cannot be given local legal effect according to their terms, reviewing courts
shall apply local law that most closely approximates an absolute waiver of
all civil liability in connection with our software and services, unless a
warranty or assumption of liability was provided in return for a fee.
## Severability ##
(a) If the Disclaimer of Warranty and Limitation of Liability set forth above
cannot be given local legal effect according to their terms, the reviewing
courts shall apply the local law that most closely approximates an absolute
waiver of any civil liability in connection with our software and services,
unless a written warranty or assumption of liability has been granted for a fee.
(b) In the event that any provision set forth above is found by a court of
competent jurisdiction to be invalid, illegal, void or unenforceable, the
remaining provisions shall remain in full force and effect and shall not be
impaired, affected or invalidated in any way.
(c) It is hereby agreed and declared that it is the intention of the parties to
enforce the remaining provisions without the inclusion of those provisions that
may subsequently be declared invalid, illegal, void or unenforceable.

View file

@ -0,0 +1,6 @@
Sample File Attribution
===========================================================================
| Filename | Author | URL |
|----------------|------------|----------------------------------------------------------------------|
| pythagoras.gif | Petrus3743 | <https://commons.wikimedia.org/wiki/File:01-Satz_des_Pythagoras.gif> |

View file

@ -0,0 +1,25 @@
[{
"SourceFile": "example.gif",
"ExifToolVersion": 12.40,
"FileName": "example.gif",
"Directory": ".",
"FileSize": 5760,
"FileModifyDate": "2021:11:01 14:39:36+00:00",
"FileAccessDate": "2022:04:12 15:49:52+00:00",
"FileInodeChangeDate": "2022:03:02 20:29:31+00:00",
"FilePermissions": 100666,
"FileType": "GIF",
"FileTypeExtension": "GIF",
"MIMEType": "image/gif",
"GIFVersion": "89a",
"ImageWidth": 100,
"ImageHeight": 67,
"HasColorMap": 1,
"ColorResolutionDepth": 8,
"BitsPerPixel": 8,
"BackgroundColor": 0,
"AnimationIterations": 0,
"TransparentColor": 255,
"ImageSize": "100 67",
"Megapixels": 0.0067
}]

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

View file

@ -0,0 +1,28 @@
[{
"SourceFile": "pythagoras.gif",
"ExifToolVersion": 12.40,
"FileName": "pythagoras.gif",
"Directory": ".",
"FileSize": 191371,
"FileModifyDate": "2022:04:13 08:42:33+00:00",
"FileAccessDate": "2022:04:13 10:52:43+00:00",
"FileInodeChangeDate": "2022:04:13 08:42:42+00:00",
"FilePermissions": 100664,
"FileType": "GIF",
"FileTypeExtension": "GIF",
"MIMEType": "image/gif",
"GIFVersion": "89a",
"ImageWidth": 591,
"ImageHeight": 639,
"HasColorMap": 1,
"ColorResolutionDepth": 7,
"BitsPerPixel": 8,
"BackgroundColor": 255,
"AnimationIterations": 0,
"Comment": "Created with GIMP",
"TransparentColor": 255,
"FrameCount": 201,
"Duration": 49.8,
"ImageSize": "591 639",
"Megapixels": 0.377649
}]

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:
@ -38,7 +38,7 @@ var version = "development"
var log = event.Log
const appDescription = "Visit https://docs.photoprism.app/ to learn more."
const appCopyright = "(c) 2018-2022 Michael Mayer <hello@photoprism.app>"
const appCopyright = "(c) 2018-2022 PhotoPrism UG. All rights reserved."
func main() {
defer func() {

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -3230,9 +3230,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001328",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001328.tgz",
"integrity": "sha512-Ue55jHkR/s4r00FLNiX+hGMMuwml/QGqqzVeMQ5thUewznU2EdULFvI3JR7JJid6OrjJNfFvHY2G2dIjmRaDDQ==",
"version": "1.0.30001331",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001331.tgz",
"integrity": "sha512-Y1xk6paHpUXKP/P6YjQv1xqyTbgAP05ycHBcRdQjTcyXlWol868sJJPlmk5ylOekw2BrucWes5jk+LvVd7WZ5Q==",
"funding": [
{
"type": "opencollective",
@ -3837,25 +3837,28 @@
}
},
"node_modules/css-loader/node_modules/lru-cache": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.1.tgz",
"integrity": "sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg==",
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=12"
"node": ">=10"
}
},
"node_modules/css-loader/node_modules/semver": {
"version": "7.3.6",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.6.tgz",
"integrity": "sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==",
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dependencies": {
"lru-cache": "^7.4.0"
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": "^10.0.0 || ^12.0.0 || ^14.0.0 || >=16.0.0"
"node": ">=10"
}
},
"node_modules/css-prefers-color-scheme": {
@ -4479,9 +4482,9 @@
}
},
"node_modules/enhanced-resolve": {
"version": "5.9.2",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz",
"integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==",
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz",
"integrity": "sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==",
"dependencies": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
@ -4523,9 +4526,9 @@
}
},
"node_modules/es-abstract": {
"version": "1.19.3",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.3.tgz",
"integrity": "sha512-4axXLNovnMYf0+csS5rVnS5hLmV1ek+ecx9MuCjByL1E5Nn54avf6CHQxIjgQIHBnfX9AMxTRIy0q+Yu5J/fXA==",
"version": "1.19.4",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.4.tgz",
"integrity": "sha512-flV8e5g9/xulChMG48Fygk1ptpo4lQRJ0eJYtxJFgi7pklLx7EFcOJ34jnvr8pbWlaFN/AT1cZpe0hiFel9Hqg==",
"dependencies": {
"call-bind": "^1.0.2",
"es-to-primitive": "^1.2.1",
@ -5192,25 +5195,28 @@
}
},
"node_modules/eslint-plugin-vue/node_modules/lru-cache": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.1.tgz",
"integrity": "sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg==",
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=12"
"node": ">=10"
}
},
"node_modules/eslint-plugin-vue/node_modules/semver": {
"version": "7.3.6",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.6.tgz",
"integrity": "sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==",
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dependencies": {
"lru-cache": "^7.4.0"
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": "^10.0.0 || ^12.0.0 || ^14.0.0 || >=16.0.0"
"node": ">=10"
}
},
"node_modules/eslint-rule-docs": {
@ -6053,19 +6059,19 @@
"integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg=="
},
"node_modules/flow-parser": {
"version": "0.175.1",
"resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.175.1.tgz",
"integrity": "sha512-gYes5/nxeLYiu02MMb+WH4KaOIYrVcTVIuV9M4aP/4hqJ+zULxxS/In+WEj/tEBsQ+8/wSHo9IDWKQL1FhrLmA==",
"version": "0.176.0",
"resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.176.0.tgz",
"integrity": "sha512-h3OUIHINMtdOnaMXyJH5ryqly1rft0Lt/di22dJB35FCnulSTxWvQhDtOH2dxQMU48/wtUmAOo4sv7Mj7XNyng==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/flow-remove-types": {
"version": "2.175.1",
"resolved": "https://registry.npmjs.org/flow-remove-types/-/flow-remove-types-2.175.1.tgz",
"integrity": "sha512-malB3a9t7zEi5TMBbQlSZ47vqH673aiN+a34Xlv6/q3fJwyaXYlIz2wa9tMa3h5kM26aH7dFRS0GeBbfV1C5IQ==",
"version": "2.176.0",
"resolved": "https://registry.npmjs.org/flow-remove-types/-/flow-remove-types-2.176.0.tgz",
"integrity": "sha512-jvNyonnqDe9v0OkLPTNeZwpgc0zaGvqFcpz3rfmgbSvynnJ8ldcv7mcYZ62UtdCbZas23bXUIxMsrgLiIkScPQ==",
"dependencies": {
"flow-parser": "^0.175.1",
"flow-parser": "^0.176.0",
"pirates": "^3.0.2",
"vlq": "^0.2.1"
},
@ -7494,9 +7500,9 @@
}
},
"node_modules/karma": {
"version": "6.3.17",
"resolved": "https://registry.npmjs.org/karma/-/karma-6.3.17.tgz",
"integrity": "sha512-2TfjHwrRExC8yHoWlPBULyaLwAFmXmxQrcuFImt/JsAsSZu1uOWTZ1ZsWjqQtWpHLiatJOHL5jFjXSJIgCd01g==",
"version": "6.3.18",
"resolved": "https://registry.npmjs.org/karma/-/karma-6.3.18.tgz",
"integrity": "sha512-YEwXVHRILKWKN7uEW9IkgTPjnYGb3YA3MDvlp04xpSRAyrNPoRmsBayLDgHykKAwBm6/mAOckj4xi/1JdQfhzQ==",
"dependencies": {
"@colors/colors": "1.5.0",
"body-parser": "^1.19.0",
@ -7517,7 +7523,7 @@
"qjobs": "^1.2.0",
"range-parser": "^1.2.1",
"rimraf": "^3.0.2",
"socket.io": "^4.2.0",
"socket.io": "^4.4.1",
"source-map": "^0.6.1",
"tmp": "^0.2.1",
"ua-parser-js": "^0.7.30",
@ -9292,25 +9298,28 @@
}
},
"node_modules/postcss-loader/node_modules/lru-cache": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.1.tgz",
"integrity": "sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg==",
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=12"
"node": ">=10"
}
},
"node_modules/postcss-loader/node_modules/semver": {
"version": "7.3.6",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.6.tgz",
"integrity": "sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==",
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dependencies": {
"lru-cache": "^7.4.0"
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": "^10.0.0 || ^12.0.0 || ^14.0.0 || >=16.0.0"
"node": ">=10"
}
},
"node_modules/postcss-logical": {
@ -12033,25 +12042,28 @@
}
},
"node_modules/vue-eslint-parser/node_modules/lru-cache": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.1.tgz",
"integrity": "sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg==",
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=12"
"node": ">=10"
}
},
"node_modules/vue-eslint-parser/node_modules/semver": {
"version": "7.3.6",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.6.tgz",
"integrity": "sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==",
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dependencies": {
"lru-cache": "^7.4.0"
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": "^10.0.0 || ^12.0.0 || ^14.0.0 || >=16.0.0"
"node": ">=10"
}
},
"node_modules/vue-fullscreen": {
@ -15132,9 +15144,9 @@
}
},
"caniuse-lite": {
"version": "1.0.30001328",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001328.tgz",
"integrity": "sha512-Ue55jHkR/s4r00FLNiX+hGMMuwml/QGqqzVeMQ5thUewznU2EdULFvI3JR7JJid6OrjJNfFvHY2G2dIjmRaDDQ=="
"version": "1.0.30001331",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001331.tgz",
"integrity": "sha512-Y1xk6paHpUXKP/P6YjQv1xqyTbgAP05ycHBcRdQjTcyXlWol868sJJPlmk5ylOekw2BrucWes5jk+LvVd7WZ5Q=="
},
"chai": {
"version": "4.3.6",
@ -15571,16 +15583,19 @@
},
"dependencies": {
"lru-cache": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.1.tgz",
"integrity": "sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg=="
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"requires": {
"yallist": "^4.0.0"
}
},
"semver": {
"version": "7.3.6",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.6.tgz",
"integrity": "sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==",
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"requires": {
"lru-cache": "^7.4.0"
"lru-cache": "^6.0.0"
}
}
}
@ -16039,9 +16054,9 @@
}
},
"enhanced-resolve": {
"version": "5.9.2",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz",
"integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==",
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz",
"integrity": "sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==",
"requires": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
@ -16071,9 +16086,9 @@
}
},
"es-abstract": {
"version": "1.19.3",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.3.tgz",
"integrity": "sha512-4axXLNovnMYf0+csS5rVnS5hLmV1ek+ecx9MuCjByL1E5Nn54avf6CHQxIjgQIHBnfX9AMxTRIy0q+Yu5J/fXA==",
"version": "1.19.4",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.4.tgz",
"integrity": "sha512-flV8e5g9/xulChMG48Fygk1ptpo4lQRJ0eJYtxJFgi7pklLx7EFcOJ34jnvr8pbWlaFN/AT1cZpe0hiFel9Hqg==",
"requires": {
"call-bind": "^1.0.2",
"es-to-primitive": "^1.2.1",
@ -16655,16 +16670,19 @@
},
"dependencies": {
"lru-cache": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.1.tgz",
"integrity": "sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg=="
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"requires": {
"yallist": "^4.0.0"
}
},
"semver": {
"version": "7.3.6",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.6.tgz",
"integrity": "sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==",
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"requires": {
"lru-cache": "^7.4.0"
"lru-cache": "^6.0.0"
}
}
}
@ -17177,16 +17195,16 @@
"integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg=="
},
"flow-parser": {
"version": "0.175.1",
"resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.175.1.tgz",
"integrity": "sha512-gYes5/nxeLYiu02MMb+WH4KaOIYrVcTVIuV9M4aP/4hqJ+zULxxS/In+WEj/tEBsQ+8/wSHo9IDWKQL1FhrLmA=="
"version": "0.176.0",
"resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.176.0.tgz",
"integrity": "sha512-h3OUIHINMtdOnaMXyJH5ryqly1rft0Lt/di22dJB35FCnulSTxWvQhDtOH2dxQMU48/wtUmAOo4sv7Mj7XNyng=="
},
"flow-remove-types": {
"version": "2.175.1",
"resolved": "https://registry.npmjs.org/flow-remove-types/-/flow-remove-types-2.175.1.tgz",
"integrity": "sha512-malB3a9t7zEi5TMBbQlSZ47vqH673aiN+a34Xlv6/q3fJwyaXYlIz2wa9tMa3h5kM26aH7dFRS0GeBbfV1C5IQ==",
"version": "2.176.0",
"resolved": "https://registry.npmjs.org/flow-remove-types/-/flow-remove-types-2.176.0.tgz",
"integrity": "sha512-jvNyonnqDe9v0OkLPTNeZwpgc0zaGvqFcpz3rfmgbSvynnJ8ldcv7mcYZ62UtdCbZas23bXUIxMsrgLiIkScPQ==",
"requires": {
"flow-parser": "^0.175.1",
"flow-parser": "^0.176.0",
"pirates": "^3.0.2",
"vlq": "^0.2.1"
},
@ -18159,9 +18177,9 @@
}
},
"karma": {
"version": "6.3.17",
"resolved": "https://registry.npmjs.org/karma/-/karma-6.3.17.tgz",
"integrity": "sha512-2TfjHwrRExC8yHoWlPBULyaLwAFmXmxQrcuFImt/JsAsSZu1uOWTZ1ZsWjqQtWpHLiatJOHL5jFjXSJIgCd01g==",
"version": "6.3.18",
"resolved": "https://registry.npmjs.org/karma/-/karma-6.3.18.tgz",
"integrity": "sha512-YEwXVHRILKWKN7uEW9IkgTPjnYGb3YA3MDvlp04xpSRAyrNPoRmsBayLDgHykKAwBm6/mAOckj4xi/1JdQfhzQ==",
"requires": {
"@colors/colors": "1.5.0",
"body-parser": "^1.19.0",
@ -18182,7 +18200,7 @@
"qjobs": "^1.2.0",
"range-parser": "^1.2.1",
"rimraf": "^3.0.2",
"socket.io": "^4.2.0",
"socket.io": "^4.4.1",
"source-map": "^0.6.1",
"tmp": "^0.2.1",
"ua-parser-js": "^0.7.30",
@ -19440,16 +19458,19 @@
},
"dependencies": {
"lru-cache": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.1.tgz",
"integrity": "sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg=="
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"requires": {
"yallist": "^4.0.0"
}
},
"semver": {
"version": "7.3.6",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.6.tgz",
"integrity": "sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==",
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"requires": {
"lru-cache": "^7.4.0"
"lru-cache": "^6.0.0"
}
}
}
@ -21417,16 +21438,19 @@
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="
},
"lru-cache": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.1.tgz",
"integrity": "sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg=="
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"requires": {
"yallist": "^4.0.0"
}
},
"semver": {
"version": "7.3.6",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.6.tgz",
"integrity": "sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==",
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"requires": {
"lru-cache": "^7.4.0"
"lru-cache": "^6.0.0"
}
}
}

View file

@ -1,8 +1,8 @@
{
"name": "photoprism",
"version": "1.0.0",
"description": "PhotoPrism Progressive Web App (PWA)",
"author": "Michael Mayer",
"description": "AI-Powered Photos App",
"author": "PhotoPrism UG",
"license": "AGPL-3.0",
"private": true,
"scripts": {

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:
@ -32,6 +32,10 @@ const Hour = 60 * Minute;
let start = new Date();
export default class Util {
static fps(fps) {
return `${fps.toFixed(1)} FPS`;
}
static duration(d) {
let u = d;

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -9,10 +9,9 @@
</v-flex>
<v-flex xs12 sm6 class="px-0 pb-2 body-1 text-xs-left text-sm-right">
<a href="https://photoprism.app/team/" target="_blank">© 2018-2022 Michael Mayer</a><br>
<a href="https://photoprism.app/team/" target="_blank">© 2018-2022 PhotoPrism UG</a><br>
<a href="https://raw.githubusercontent.com/photoprism/photoprism/develop/NOTICE"
target="_blank" class="text-link">
3rd-party software packages</a>
target="_blank" class="text-link">3rd-party software packages</a>
</v-flex>
</v-layout>
</v-card-actions>

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -106,6 +106,15 @@
</v-list-tile-content>
</v-list-tile>
<v-list-tile :to="{name: 'browse', query: { q: 'type:animated' }}" :exact="true" class="nav-animated"
@click.stop="">
<v-list-tile-content>
<v-list-tile-title :class="`menu-item ${rtl ? '--rtl' : ''}`">
<translate>Animated</translate>
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
<v-list-tile :to="{name: 'photos', query: { q: 'stack:true' }}" :exact="true" class="nav-stacks" @click.stop="">
<v-list-tile-content>
<v-list-tile-title :class="`p-flex-menuitem menu-item ${rtl ? '--rtl' : ''}`">

View file

@ -46,7 +46,7 @@
@mouseover="playLive(photo)"
@mouseleave="pauseLive(photo)"
>
<v-layout v-if="photo.Type === 'live'" class="live-player">
<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>
<source :src="photo.videoUrl()">
@ -153,7 +153,7 @@
{{ photo.getDateString(true) }}
</button>
<br>
<button v-if="photo.Type === 'video'" :title="labels.video"
<button v-if="photo.Type === 'video' || photo.Type === 'animated'" :title="labels.video"
@click.exact="openPhoto(index, true)">
<v-icon size="14">movie</v-icon>
{{ photo.getVideoInfo() }}

View file

@ -52,7 +52,7 @@
<v-icon color="white" class="select-on">check_circle</v-icon>
<v-icon color="white" class="select-off">radio_button_off</v-icon>
</v-btn>
<v-btn v-else-if="props.item.Type === 'video' || props.item.Type === 'live'"
<v-btn v-else-if="props.item.Type === 'video' || props.item.Type === 'live' || props.item.Type === 'animated'"
:ripple="false"
flat icon large absolute class="input-open"
@click.stop.prevent="openPhoto(props.index, true)">
@ -211,7 +211,7 @@ export default {
} else if (this.photos[index]) {
let photo = this.photos[index];
if (photo.Type === 'video' && photo.isPlayable()) {
if ((photo.Type === 'video' || photo.Type === 'animated') && photo.isPlayable()) {
this.openPhoto(index, true);
} else {
this.openPhoto(index, false);

View file

@ -45,7 +45,7 @@
@mouseover="playLive(photo)"
@mouseleave="pauseLive(photo)"
>
<v-layout v-if="photo.Type === 'live'" class="live-player">
<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>
<source :src="photo.videoUrl()">

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -300,6 +300,7 @@ body.chrome #photoprism .search-results .result {
#photoprism .search-results.select-results .type-image .input-view,
#photoprism .search-results .type-raw .input-open,
#photoprism .search-results .type-video.is-playable .input-open,
#photoprism .search-results .type-animated.is-playable .input-open,
#photoprism .search-results .type-live.is-playable .input-open,
#photoprism .search-results .type-image.is-stack .input-open {
display: inline-flex;
@ -311,6 +312,7 @@ body.chrome #photoprism .search-results .result {
#photoprism .search-results .type-raw .input-open .action-raw,
#photoprism .search-results .type-video.is-playable .input-open .action-play,
#photoprism .search-results .type-animated.is-playable .input-open .action-play,
#photoprism .search-results .type-live.is-playable .input-open .action-live,
#photoprism .search-results .type-image.is-stack .input-open .action-stack {
display: inline-flex;
@ -320,6 +322,7 @@ body.chrome #photoprism .search-results .result {
display: none;
}
#photoprism .search-results .type-animated.is-playable:hover .live-player,
#photoprism .search-results .type-live.is-playable:hover .live-player {
display: flex;
overflow: hidden !important;
@ -330,6 +333,7 @@ body.chrome #photoprism .search-results .result {
}
#photoprism .search-results.list-view .type-video.is-playable .input-play,
#photoprism .search-results.list-view .type-animated.is-playable .input-play,
#photoprism .search-results.list-view .type-live.is-playable .input-play {
display: inline-flex;
opacity: 0.75;

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -21,7 +21,7 @@
<div class="v-table__overflow">
<table class="v-datatable v-table theme--light photo-files">
<tbody>
<tr v-if="file.Type === 'jpg'">
<tr v-if="file.FileType === 'jpg'">
<td>
<translate>Preview</translate>
</td>
@ -45,7 +45,7 @@
@click.stop.prevent="downloadFile(file)">
<translate>Download</translate>
</v-btn>
<v-btn v-if="features.edit && file.Type === 'jpg' && !file.Error && !file.Primary" small depressed dark
<v-btn v-if="features.edit && file.FileType === 'jpg' && !file.Error && !file.Primary" small depressed dark
color="primary-button"
class="ma-0 action-primary"
@click.stop.prevent="primaryFile(file)">
@ -106,7 +106,7 @@
</td>
<td>{{ file.sizeInfo() }}</td>
</tr>
<tr v-if="file.Type">
<tr v-if="file.FileType">
<td>
<translate>Type</translate>
</td>
@ -118,6 +118,18 @@
</td>
<td>{{ codecName(file) }}</td>
</tr>
<tr v-if="file.Frames">
<td>
<translate>Frames</translate>
</td>
<td>{{ file.Frames }}</td>
</tr>
<tr v-if="file.FPS">
<td>
<translate>FPS</translate>
</td>
<td>{{ file.FPS.toFixed(1) }}</td>
</tr>
<tr v-if="file.Primary">
<td>
<translate>Primary</translate>

View file

@ -41,15 +41,15 @@ msgstr ""
msgid "%{n} pictures found"
msgstr ""
#: src/options/options.js:371
#: src/options/options.js:375
msgid "1 hour"
msgstr ""
#: src/options/options.js:373
#: src/options/options.js:377
msgid "12 hours"
msgstr ""
#: src/options/options.js:372
#: src/options/options.js:376
msgid "4 hours"
msgstr ""
@ -57,7 +57,7 @@ msgstr ""
msgid "A click will copy it to your clipboard."
msgstr ""
#: src/component/navigation.vue:398
#: src/component/navigation.vue:406
#: src/component/navigation.vue:27
#: src/pages/about/about.vue:4
#: src/pages/about/about.vue:183
@ -120,8 +120,8 @@ msgstr ""
msgid "Add to album"
msgstr ""
#: src/dialog/photo/files.vue:171
#: src/dialog/photo/files.vue:168
#: src/dialog/photo/files.vue:183
#: src/dialog/photo/files.vue:180
msgid "Added"
msgstr ""
@ -129,23 +129,23 @@ msgstr ""
msgid "Advanced"
msgstr ""
#: src/options/options.js:381
#: src/options/options.js:385
msgid "After 1 day"
msgstr ""
#: src/options/options.js:382
#: src/options/options.js:386
msgid "After 3 days"
msgstr ""
#: src/options/options.js:383
#: src/options/options.js:387
msgid "After 7 days"
msgstr ""
#: src/options/options.js:385
#: src/options/options.js:389
msgid "After one month"
msgstr ""
#: src/options/options.js:387
#: src/options/options.js:391
msgid "After one year"
msgstr ""
@ -154,11 +154,11 @@ msgstr ""
msgid "After selecting pictures from search results, you can add them to an album using the context menu."
msgstr ""
#: src/options/options.js:386
#: src/options/options.js:390
msgid "After two months"
msgstr ""
#: src/options/options.js:384
#: src/options/options.js:388
msgid "After two weeks"
msgstr ""
@ -173,10 +173,10 @@ msgstr ""
#: src/app/routes.js:129
#: src/app/routes.js:136
#: src/component/navigation.vue:136
#: src/component/navigation.vue:146
#: src/component/navigation.vue:144
#: src/component/navigation.vue:154
#: src/component/navigation.vue:4
#: src/component/navigation.vue:572
#: src/component/navigation.vue:601
#: src/share/albums.vue:4
msgid "Albums"
msgstr ""
@ -264,6 +264,12 @@ msgstr ""
msgid "An error occurred - are you offline?"
msgstr ""
#: src/component/navigation.vue:98
#: src/options/options.js:310
msgid "Animated"
msgstr ""
#: src/model/file.js:189
#: src/pages/settings/general.vue:533
msgid "Animation"
msgstr ""
@ -288,7 +294,7 @@ msgid "Approve"
msgstr ""
#: src/app/routes.js:210
#: src/component/navigation.vue:123
#: src/component/navigation.vue:131
#: src/component/photo/cards.vue:36
#: src/component/photo/clipboard.vue:216
#: src/pages/settings/general.vue:294
@ -332,8 +338,8 @@ msgstr ""
msgid "Artist"
msgstr ""
#: src/dialog/photo/files.vue:131
#: src/dialog/photo/files.vue:128
#: src/dialog/photo/files.vue:143
#: src/dialog/photo/files.vue:140
msgid "Aspect Ratio"
msgstr ""
@ -358,19 +364,19 @@ msgstr ""
msgid "Bio"
msgstr ""
#: src/options/options.js:406
#: src/options/options.js:410
msgid "Black"
msgstr ""
#: src/options/options.js:419
#: src/options/options.js:423
msgid "Blackman: Lanczos Modification, Less Ringing Artifacts"
msgstr ""
#: src/options/options.js:402
#: src/options/options.js:406
msgid "Blue"
msgstr ""
#: src/options/options.js:403
#: src/options/options.js:407
msgid "Brown"
msgstr ""
@ -382,7 +388,7 @@ msgstr ""
msgid "Browse indexed files and folders in Library."
msgstr ""
#: src/options/options.js:413
#: src/options/options.js:417
msgid "Bug Report"
msgstr ""
@ -392,8 +398,8 @@ msgstr ""
#: src/app/routes.js:142
#: src/app/routes.js:149
#: src/component/navigation.vue:242
#: src/component/navigation.vue:934
#: src/component/navigation.vue:250
#: src/component/navigation.vue:963
msgid "Calendar"
msgstr ""
@ -456,7 +462,7 @@ msgstr ""
msgid "Cards"
msgstr ""
#: src/component/photo/toolbar.vue:351
#: src/component/photo/toolbar.vue:352
#: src/dialog/album/edit.vue:162
#: src/pages/about/feedback.vue:108
#: src/pages/albums.vue:130
@ -479,8 +485,8 @@ msgstr ""
msgid "Checked"
msgstr ""
#: src/dialog/photo/files.vue:157
#: src/dialog/photo/files.vue:154
#: src/dialog/photo/files.vue:169
#: src/dialog/photo/files.vue:166
msgid "Chroma"
msgstr ""
@ -496,12 +502,12 @@ msgstr ""
msgid "Codec"
msgstr ""
#: src/component/photo/toolbar.vue:327
#: src/component/photo/toolbar.vue:328
msgid "Color"
msgstr ""
#: src/dialog/photo/files.vue:145
#: src/dialog/photo/files.vue:142
#: src/dialog/photo/files.vue:157
#: src/dialog/photo/files.vue:154
msgid "Color Profile"
msgstr ""
@ -581,7 +587,7 @@ msgstr ""
msgid "Creating thumbnails for"
msgstr ""
#: src/options/options.js:421
#: src/options/options.js:425
msgid "Cubic: Moderate Quality, Good Performance"
msgstr ""
@ -589,11 +595,11 @@ msgstr ""
msgid "Current Password"
msgstr ""
#: src/options/options.js:410
#: src/options/options.js:414
msgid "Customer Support"
msgstr ""
#: src/options/options.js:401
#: src/options/options.js:405
msgid "Cyan"
msgstr ""
@ -601,7 +607,7 @@ msgstr ""
msgid "Cyano"
msgstr ""
#: src/options/options.js:374
#: src/options/options.js:378
msgid "Daily"
msgstr ""
@ -614,7 +620,7 @@ msgid "Debug Logs"
msgstr ""
#: src/options/options.js:194
#: src/options/options.js:325
#: src/options/options.js:329
msgid "Default"
msgstr ""
@ -632,7 +638,7 @@ msgstr ""
#: src/dialog/photo/delete.vue:18
#: src/dialog/photo/files.vue:41
#: src/dialog/photo/files.vue:38
#: src/dialog/share.vue:234
#: src/dialog/share.vue:235
#: src/pages/library/errors.vue:94
#: src/pages/settings/general.vue:206
msgid "Delete"
@ -741,7 +747,7 @@ msgstr ""
msgid "Don't use TensorFlow for image classification."
msgstr ""
#: src/options/options.js:414
#: src/options/options.js:418
msgid "Donations"
msgstr ""
@ -859,7 +865,7 @@ msgstr ""
msgid "Enables RAW converter presets. May reduce performance."
msgstr ""
#: src/component/navigation.vue:365
#: src/component/navigation.vue:373
msgid "Errors"
msgstr ""
@ -871,7 +877,7 @@ msgstr ""
msgid "Estimates"
msgstr ""
#: src/options/options.js:375
#: src/options/options.js:379
msgid "Every two days"
msgstr ""
@ -884,7 +890,7 @@ msgstr ""
msgid "Exclude hidden"
msgstr ""
#: src/component/navigation.vue:279
#: src/component/navigation.vue:280
msgid "Expand"
msgstr ""
@ -943,16 +949,16 @@ msgid "Favorite"
msgstr ""
#: src/app/routes.js:175
#: src/component/navigation.vue:216
#: src/component/navigation.vue:846
#: src/component/navigation.vue:224
#: src/component/navigation.vue:875
msgid "Favorites"
msgstr ""
#: src/options/options.js:412
#: src/options/options.js:416
msgid "Feature Request"
msgstr ""
#: src/component/navigation.vue:406
#: src/component/navigation.vue:414
#: src/component/navigation.vue:35
msgid "Feedback"
msgstr ""
@ -961,7 +967,7 @@ msgstr ""
msgid "Feel free to contact us at hello@photoprism.app if you have any questions."
msgstr ""
#: src/model/file.js:241
#: src/model/file.js:254
msgid "File"
msgstr ""
@ -1002,8 +1008,8 @@ msgstr ""
#: src/app/routes.js:155
#: src/app/routes.js:162
#: src/component/navigation.vue:302
#: src/component/navigation.vue:1142
#: src/component/navigation.vue:310
#: src/component/navigation.vue:1171
msgid "Folders"
msgstr ""
@ -1011,6 +1017,16 @@ msgstr ""
msgid "Forgot password?"
msgstr ""
#: src/dialog/photo/files.vue:107
#: src/dialog/photo/files.vue:104
msgid "FPS"
msgstr ""
#: src/dialog/photo/files.vue:101
#: src/dialog/photo/files.vue:98
msgid "Frames"
msgstr ""
#: src/component/photo-viewer.vue:174
msgid "Fullscreen"
msgstr ""
@ -1027,7 +1043,7 @@ msgstr ""
msgid "Getting Support"
msgstr ""
#: src/options/options.js:396
#: src/options/options.js:400
msgid "Gold"
msgstr ""
@ -1035,11 +1051,11 @@ msgstr ""
msgid "Grayscale"
msgstr ""
#: src/options/options.js:399
#: src/options/options.js:403
msgid "Green"
msgstr ""
#: src/options/options.js:405
#: src/options/options.js:409
msgid "Grey"
msgstr ""
@ -1058,7 +1074,7 @@ msgstr ""
msgid "Help"
msgstr ""
#: src/component/navigation.vue:356
#: src/component/navigation.vue:364
msgid "Hidden"
msgstr ""
@ -1075,12 +1091,12 @@ msgstr ""
msgid "Hide photos that have been moved to archive."
msgstr ""
#: src/options/options.js:329
#: src/options/options.js:333
msgid "High"
msgstr ""
#: src/dialog/photo/files.vue:109
#: src/dialog/photo/files.vue:106
#: src/dialog/photo/files.vue:121
#: src/dialog/photo/files.vue:118
msgid "High Dynamic Range (HDR)"
msgstr ""
@ -1138,10 +1154,10 @@ msgstr ""
msgid "Importing files to originals…"
msgstr ""
#: src/dialog/photo/files.vue:174
#: src/dialog/photo/files.vue:186
#: src/dialog/photo/files.vue:195
#: src/dialog/photo/files.vue:183
#: src/dialog/photo/files.vue:171
#: src/dialog/photo/files.vue:180
#: src/dialog/photo/files.vue:192
msgid "in"
msgstr ""
@ -1218,8 +1234,8 @@ msgid "Label"
msgstr ""
#: src/app/routes.js:261
#: src/component/navigation.vue:289
#: src/component/navigation.vue:1097
#: src/component/navigation.vue:297
#: src/component/navigation.vue:1126
#: src/dialog/photo/edit.vue:39
#: src/dialog/photo/edit.vue:6
#: src/dialog/photo/edit.vue:215
@ -1231,7 +1247,7 @@ msgstr ""
msgid "Labels deleted"
msgstr ""
#: src/options/options.js:420
#: src/options/options.js:424
msgid "Lanczos: Detail Preservation, Minimal Artifacts"
msgstr ""
@ -1259,16 +1275,16 @@ msgstr ""
#: src/app/routes.js:293
#: src/app/routes.js:300
#: src/app/routes.js:307
#: src/component/navigation.vue:328
#: src/component/navigation.vue:338
#: src/component/navigation.vue:336
#: src/component/navigation.vue:346
#: src/component/navigation.vue:4
#: src/component/navigation.vue:1234
#: src/component/navigation.vue:1263
#: src/pages/settings.vue:41
#: src/pages/settings/general.vue:404
msgid "Library"
msgstr ""
#: src/component/navigation.vue:414
#: src/component/navigation.vue:422
#: src/component/navigation.vue:43
#: src/dialog/photo/details.vue:544
#: src/pages/about/license.vue:4
@ -1279,7 +1295,7 @@ msgstr ""
msgid "Like"
msgstr ""
#: src/options/options.js:398
#: src/options/options.js:402
msgid "Lime"
msgstr ""
@ -1287,7 +1303,7 @@ msgstr ""
msgid "Limit reached, showing first %{n} files"
msgstr ""
#: src/options/options.js:422
#: src/options/options.js:426
msgid "Linear: Very Smooth, Best Performance"
msgstr ""
@ -1301,11 +1317,11 @@ msgid "List"
msgstr ""
#: src/app/routes.js:182
#: src/component/navigation.vue:189
#: src/component/navigation.vue:197
#: src/component/photo/cards.vue:215
#: src/component/photo/list.vue:186
#: src/component/photo/mosaic.vue:190
#: src/options/options.js:314
#: src/options/options.js:318
#: src/share/photo/cards.vue:188
#: src/share/photo/list.vue:165
#: src/share/photo/mosaic.vue:171
@ -1334,14 +1350,14 @@ msgstr ""
msgid "Log messages appear here whenever PhotoPrism comes across broken files, or there are other potential issues."
msgstr ""
#: src/component/navigation.vue:428
#: src/component/navigation.vue:1570
#: src/component/navigation.vue:436
#: src/component/navigation.vue:1599
msgid "Login"
msgstr ""
#: src/component/navigation.vue:475
#: src/component/navigation.vue:1680
#: src/component/navigation.vue:1711
#: src/component/navigation.vue:483
#: src/component/navigation.vue:1709
#: src/component/navigation.vue:1740
msgid "Logout"
msgstr ""
@ -1355,16 +1371,16 @@ msgstr ""
msgid "Longitude"
msgstr ""
#: src/options/options.js:333
#: src/options/options.js:337
msgid "Low"
msgstr ""
#: src/options/options.js:392
#: src/options/options.js:396
msgid "Magenta"
msgstr ""
#: src/dialog/photo/files.vue:151
#: src/dialog/photo/files.vue:148
#: src/dialog/photo/files.vue:163
#: src/dialog/photo/files.vue:160
msgid "Main Color"
msgstr ""
@ -1392,19 +1408,19 @@ msgstr ""
msgid "Message sent"
msgstr ""
#: src/component/navigation.vue:232
#: src/component/navigation.vue:233
msgid "Minimize"
msgstr ""
#: src/dialog/photo/files.vue:163
#: src/dialog/photo/files.vue:160
#: src/dialog/photo/files.vue:175
#: src/dialog/photo/files.vue:172
msgid "Missing"
msgstr ""
#: src/app/routes.js:116
#: src/app/routes.js:123
#: src/component/navigation.vue:229
#: src/component/navigation.vue:889
#: src/component/navigation.vue:237
#: src/component/navigation.vue:918
#: src/pages/settings/general.vue:360
msgid "Moments"
msgstr ""
@ -1496,8 +1512,8 @@ msgstr ""
msgid "Name too long"
msgstr ""
#: src/options/options.js:370
#: src/options/options.js:380
#: src/options/options.js:374
#: src/options/options.js:384
#: src/pages/settings/sync.vue:50
msgid "Never"
msgstr ""
@ -1604,7 +1620,7 @@ msgid "Non-photographic and low-quality images require a review before they appe
msgstr ""
#: src/options/options.js:261
#: src/options/options.js:337
#: src/options/options.js:341
msgid "None"
msgstr ""
@ -1636,8 +1652,8 @@ msgstr ""
msgid "Nothing to see here yet. Be patient."
msgstr ""
#: src/component/navigation.vue:444
#: src/component/navigation.vue:1615
#: src/component/navigation.vue:452
#: src/component/navigation.vue:1644
#: src/options/options.js:280
msgid "Offline"
msgstr ""
@ -1648,7 +1664,7 @@ msgstr ""
msgid "Oldest first"
msgstr ""
#: src/options/options.js:376
#: src/options/options.js:380
msgid "Once a week"
msgstr ""
@ -1697,12 +1713,12 @@ msgstr ""
msgid "or ask in our Community Chat"
msgstr ""
#: src/options/options.js:395
#: src/options/options.js:399
msgid "Orange"
msgstr ""
#: src/dialog/photo/files.vue:137
#: src/dialog/photo/files.vue:134
#: src/dialog/photo/files.vue:149
#: src/dialog/photo/files.vue:146
msgid "Orientation"
msgstr ""
@ -1716,14 +1732,14 @@ msgstr ""
msgid "Original Name"
msgstr ""
#: src/component/navigation.vue:347
#: src/component/navigation.vue:355
#: src/dialog/account/edit.vue:104
#: src/pages/library/files.vue:6
#: src/pages/settings/general.vue:316
msgid "Originals"
msgstr ""
#: src/options/options.js:415
#: src/options/options.js:419
msgid "Other"
msgstr ""
@ -1757,8 +1773,8 @@ msgstr ""
#: src/app/routes.js:267
#: src/app/routes.js:287
#: src/component/navigation.vue:203
#: src/component/navigation.vue:801
#: src/component/navigation.vue:211
#: src/component/navigation.vue:830
#: src/dialog/photo/edit.vue:52
#: src/dialog/photo/edit.vue:6
#: src/dialog/photo/edit.vue:266
@ -1778,7 +1794,7 @@ msgstr ""
msgid "Permanently remove files to free up storage."
msgstr ""
#: src/model/photo.js:925
#: src/model/photo.js:958
msgid "Photo"
msgstr ""
@ -1794,7 +1810,7 @@ msgstr ""
msgid "Photos"
msgstr ""
#: src/options/options.js:393
#: src/options/options.js:397
msgid "Pink"
msgstr ""
@ -1810,10 +1826,10 @@ msgstr ""
#: src/app/routes.js:223
#: src/app/routes.js:229
#: src/app/routes.js:236
#: src/component/navigation.vue:255
#: src/component/navigation.vue:265
#: src/component/navigation.vue:263
#: src/component/navigation.vue:273
#: src/component/navigation.vue:4
#: src/component/navigation.vue:979
#: src/component/navigation.vue:1008
#: src/pages/settings/general.vue:108
#: src/pages/settings/general.vue:450
msgid "Places"
@ -1834,8 +1850,8 @@ msgstr ""
msgid "Please don't upload photos containing offensive content."
msgstr ""
#: src/dialog/photo/files.vue:117
#: src/dialog/photo/files.vue:114
#: src/dialog/photo/files.vue:129
#: src/dialog/photo/files.vue:126
msgid "Portrait"
msgstr ""
@ -1866,31 +1882,31 @@ msgid "Preview"
msgstr ""
#: src/dialog/photo/files.vue:35
#: src/dialog/photo/files.vue:101
#: src/dialog/photo/files.vue:113
#: src/dialog/photo/files.vue:32
#: src/dialog/photo/files.vue:98
#: src/dialog/photo/files.vue:110
#: src/dialog/photo/files.vue:28
msgid "Primary"
msgstr ""
#: src/app/routes.js:203
#: src/component/navigation.vue:315
#: src/component/navigation.vue:1187
#: src/component/navigation.vue:323
#: src/component/navigation.vue:1216
#: src/dialog/photo/info.vue:102
#: src/pages/settings/general.vue:272
msgid "Private"
msgstr ""
#: src/options/options.js:411
#: src/options/options.js:415
msgid "Product Feedback"
msgstr ""
#: src/dialog/photo/files.vue:125
#: src/dialog/photo/files.vue:122
#: src/dialog/photo/files.vue:137
#: src/dialog/photo/files.vue:134
msgid "Projection"
msgstr ""
#: src/options/options.js:391
#: src/options/options.js:395
msgid "Purple"
msgstr ""
@ -1910,7 +1926,7 @@ msgstr ""
msgid "Raspberry"
msgstr ""
#: src/options/options.js:310
#: src/options/options.js:314
msgid "Raw"
msgstr ""
@ -1959,7 +1975,7 @@ msgstr ""
msgid "Recognizes faces so that specific people can be found."
msgstr ""
#: src/options/options.js:394
#: src/options/options.js:398
msgid "Red"
msgstr ""
@ -2028,7 +2044,7 @@ msgid "Retype Password"
msgstr ""
#: src/app/routes.js:196
#: src/component/navigation.vue:114
#: src/component/navigation.vue:122
msgid "Review"
msgstr ""
@ -2042,7 +2058,7 @@ msgstr ""
msgid "Scan"
msgstr ""
#: src/component/navigation.vue:106
#: src/component/navigation.vue:114
msgid "Scans"
msgstr ""
@ -2050,7 +2066,7 @@ msgstr ""
#: src/component/navigation.vue:62
#: src/component/navigation.vue:72
#: src/component/navigation.vue:4
#: src/component/navigation.vue:311
#: src/component/navigation.vue:312
#: src/component/photo/toolbar.vue:50
#: src/pages/albums.vue:114
#: src/pages/labels.vue:81
@ -2119,12 +2135,12 @@ msgstr ""
#: src/app/routes.js:339
#: src/app/routes.js:351
#: src/app/routes.js:363
#: src/component/navigation.vue:379
#: src/component/navigation.vue:389
#: src/component/navigation.vue:387
#: src/component/navigation.vue:397
#: src/component/navigation.vue:8
#: src/component/navigation.vue:18
#: src/component/navigation.vue:4
#: src/component/navigation.vue:1408
#: src/component/navigation.vue:1437
msgid "Settings"
msgstr ""
@ -2191,7 +2207,7 @@ msgstr ""
msgid "Shows more detailed log messages. Requires a restart."
msgstr ""
#: src/model/file.js:182
#: src/model/file.js:195
msgid "Sidecar"
msgstr ""
@ -2256,7 +2272,7 @@ msgstr ""
msgid "Stackable"
msgstr ""
#: src/component/navigation.vue:98
#: src/component/navigation.vue:106
#: src/pages/settings/library.vue:34
msgid "Stacks"
msgstr ""
@ -2273,7 +2289,7 @@ msgstr ""
msgid "Start/Stop Slideshow"
msgstr ""
#: src/component/navigation.vue:275
#: src/component/navigation.vue:283
msgid "States"
msgstr ""
@ -2324,7 +2340,7 @@ msgstr ""
msgid "Taken"
msgstr ""
#: src/options/options.js:400
#: src/options/options.js:404
msgid "Teal"
msgstr ""
@ -2451,12 +2467,12 @@ msgstr ""
#: src/dialog/photo/details.vue:16
#: src/dialog/photo/info.vue:24
#: src/model/album.js:179
#: src/model/photo.js:585
#: src/model/photo.js:602
#: src/model/photo.js:625
#: src/model/photo.js:639
#: src/model/photo.js:724
#: src/model/photo.js:737
#: src/model/photo.js:611
#: src/model/photo.js:628
#: src/model/photo.js:651
#: src/model/photo.js:665
#: src/model/photo.js:750
#: src/model/photo.js:763
#: src/options/options.js:20
#: src/options/options.js:34
#: src/options/options.js:51
@ -2469,7 +2485,7 @@ msgid "Unknown"
msgstr ""
#: src/app/routes.js:168
#: src/component/navigation.vue:156
#: src/component/navigation.vue:164
msgid "Unsorted"
msgstr ""
@ -2478,8 +2494,8 @@ msgstr ""
msgid "Unstack"
msgstr ""
#: src/dialog/photo/files.vue:180
#: src/dialog/photo/files.vue:177
#: src/dialog/photo/files.vue:192
#: src/dialog/photo/files.vue:189
#: src/dialog/photo/info.vue:175
msgid "Updated"
msgstr ""
@ -2505,7 +2521,7 @@ msgid "Updating stacks"
msgstr ""
#: src/component/album/toolbar.vue:176
#: src/component/navigation.vue:132
#: src/component/navigation.vue:133
#: src/component/photo/toolbar.vue:128
#: src/dialog/share/upload.vue:35
#: src/dialog/upload.vue:8
@ -2581,10 +2597,10 @@ msgstr ""
#: src/component/photo/cards.vue:225
#: src/component/photo/list.vue:196
#: src/component/photo/mosaic.vue:200
#: src/model/file.js:180
#: src/model/photo.js:676
#: src/model/photo.js:690
#: src/options/options.js:318
#: src/model/file.js:193
#: src/model/photo.js:702
#: src/model/photo.js:716
#: src/options/options.js:322
#: src/share/photo/cards.vue:38
#: src/share/photo/cards.vue:198
#: src/share/photo/list.vue:175
@ -2593,10 +2609,10 @@ msgid "Video"
msgstr ""
#: src/app/routes.js:189
#: src/component/navigation.vue:169
#: src/component/navigation.vue:179
#: src/component/navigation.vue:177
#: src/component/navigation.vue:187
#: src/component/navigation.vue:4
#: src/component/navigation.vue:685
#: src/component/navigation.vue:714
msgid "Videos"
msgstr ""
@ -2629,7 +2645,7 @@ msgstr ""
msgid "WebDAV Upload"
msgstr ""
#: src/options/options.js:404
#: src/options/options.js:408
msgid "White"
msgstr ""
@ -2638,7 +2654,7 @@ msgstr ""
msgid "Year"
msgstr ""
#: src/options/options.js:397
#: src/options/options.js:401
msgid "Yellow"
msgstr ""
@ -2649,14 +2665,14 @@ msgstr ""
#: src/dialog/confirm.vue:18
#: src/dialog/people/merge.vue:18
#: src/dialog/photo/archive.vue:18
#: src/dialog/photo/files.vue:104
#: src/dialog/photo/files.vue:112
#: src/dialog/photo/files.vue:120
#: src/dialog/photo/files.vue:166
#: src/dialog/photo/files.vue:101
#: src/dialog/photo/files.vue:109
#: src/dialog/photo/files.vue:117
#: src/dialog/photo/files.vue:163
#: src/dialog/photo/files.vue:116
#: src/dialog/photo/files.vue:124
#: src/dialog/photo/files.vue:132
#: src/dialog/photo/files.vue:178
#: src/dialog/photo/files.vue:113
#: src/dialog/photo/files.vue:121
#: src/dialog/photo/files.vue:129
#: src/dialog/photo/files.vue:175
#: src/dialog/photo/info.vue:287
#: src/dialog/photo/info.vue:308
#: src/dialog/photo/info.vue:328

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:
@ -30,6 +30,7 @@ import Util from "common/util";
import { config } from "app/session";
import { $gettext } from "common/vm";
import download from "common/download";
import { MediaAnimated } from "./photo";
export class File extends RestModel {
getDefaults() {
@ -44,7 +45,8 @@ export class File extends RestModel {
Size: 0,
ModTime: 0,
Codec: "",
Type: "",
FileType: "",
MediaType: "",
Mime: "",
Primary: false,
Sidecar: false,
@ -52,19 +54,22 @@ export class File extends RestModel {
Portrait: false,
Video: false,
Duration: 0,
FPS: 0.0,
Frames: 0,
Width: 0,
Height: 0,
Orientation: 0,
Projection: "",
AspectRatio: 1.0,
HDR: false,
Watermark: false,
ColorProfile: "",
MainColor: "",
Colors: "",
Luminance: "",
Diff: 0,
Chroma: 0,
Notes: "",
Software: "",
Error: "",
Markers: [],
CreatedAt: "",
@ -112,7 +117,7 @@ export class File extends RestModel {
thumbnailUrl(size) {
if (this.Error || this.Missing) {
return `${config.contentUri}/svg/broken`;
} else if (this.Type === "raw") {
} else if (this.FileType === "raw") {
return `${config.contentUri}/svg/raw`;
} else if (this.Sidecar) {
return `${config.contentUri}/svg/file`;
@ -162,27 +167,35 @@ export class File extends RestModel {
getInfo() {
let info = [];
if (this.Type) {
info.push(this.Type.toUpperCase());
if (this.FileType) {
info.push(this.FileType.toUpperCase());
}
if (this.Duration > 0) {
info.push(Util.duration(this.Duration));
}
if (this.FPS > 0) {
info.push(Util.fps(this.FPS));
}
this.addSizeInfo(info);
return info.join(", ");
}
typeInfo() {
if (this.Type === MediaAnimated) {
return $gettext("Animation");
}
if (this.Video) {
return $gettext("Video");
} else if (this.Sidecar) {
return $gettext("Sidecar");
}
return this.Type.toUpperCase();
return this.FileType.toUpperCase();
}
sizeInfo() {

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:
@ -39,11 +39,13 @@ import * as src from "common/src";
export const CodecAvc1 = "avc1";
export const FormatMp4 = "mp4";
export const FormatAvc = "avc";
export const FormatGif = "gif";
export const FormatJpeg = "jpg";
export const TypeImage = "image";
export const TypeVideo = "video";
export const TypeLive = "live";
export const TypeRaw = "raw";
export const MediaImage = "image";
export const MediaAnimated = "animated";
export const MediaVideo = "video";
export const MediaLive = "live";
export const MediaRaw = "raw";
export const YearUnknown = -1;
export const MonthUnknown = -1;
export const DayUnknown = -1;
@ -84,7 +86,7 @@ export class Photo extends RestModel {
ID: "",
UID: "",
DocumentID: "",
Type: TypeImage,
Type: MediaImage,
TypeSrc: "",
Stack: 0,
Favorite: false,
@ -138,6 +140,8 @@ export class Photo extends RestModel {
CopyrightSrc: "",
License: "",
LicenseSrc: "",
Software: "",
SoftwareSrc: "",
},
Files: [],
Labels: [],
@ -157,6 +161,10 @@ export class Photo extends RestModel {
FileUID: "",
FileRoot: "",
FileName: "",
FileType: "",
MediaType: "",
FPS: 0.0,
Frames: 0,
Hash: "",
Width: "",
Height: "",
@ -343,7 +351,9 @@ export class Photo extends RestModel {
}
isPlayable() {
if (!this.Files) {
if (this.Type === MediaAnimated) {
return true;
} else if (!this.Files) {
return false;
}
@ -360,6 +370,10 @@ export class Photo extends RestModel {
let main = this.mainFile();
let file = this.videoFile();
if (!file) {
file = main;
}
const vw = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
const vh = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
@ -393,7 +407,7 @@ export class Photo extends RestModel {
height = newHeight;
}
const loop = file.Duration >= 0 && file.Duration <= 5000000000;
const loop = this.Type === MediaAnimated || (file.Duration >= 0 && file.Duration <= 5000000000);
const poster = this.thumbnailUrl("fit_720");
const error = false;
@ -408,18 +422,30 @@ export class Photo extends RestModel {
let file = this.Files.find((f) => f.Codec === CodecAvc1);
if (!file) {
file = this.Files.find((f) => f.Type === FormatMp4);
file = this.Files.find((f) => f.FileType === FormatMp4);
}
if (!file) {
file = this.Files.find((f) => !!f.Video);
}
if (!file) {
file = this.gifFile();
}
return file;
}
gifFile() {
if (!this.Files) {
return false;
}
return this.Files.find((f) => f.FileType === FormatGif);
}
videoUrl() {
const file = this.videoFile();
let file = this.videoFile();
if (file) {
return `${config.apiUri}/videos/${file.Hash}/${config.previewToken()}/${FormatAvc}`;
@ -439,7 +465,7 @@ export class Photo extends RestModel {
return file;
}
return this.Files.find((f) => f.Type === FormatJpeg);
return this.Files.find((f) => f.FileType === FormatJpeg);
}
jpegFiles() {
@ -447,7 +473,7 @@ export class Photo extends RestModel {
return [this];
}
return this.Files.filter((f) => f.Type === FormatJpeg);
return this.Files.filter((f) => f.FileType === FormatJpeg);
}
mainFileHash() {
@ -542,14 +568,14 @@ export class Photo extends RestModel {
}
// Skip RAW images.
if (!settings.download.raw && file.Type === TypeRaw) {
if (!settings.download.raw && file.FileType === MediaRaw) {
if (config.debug) console.log("download: skipped raw", file);
return;
}
// Skip related images if video.
// see https://github.com/photoprism/photoprism/issues/1436
if (this.Type === TypeVideo && !file.Video) {
if (this.Type === MediaVideo && !file.Video) {
if (config.debug) console.log("download: skipped image", file);
return;
}

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:
@ -193,7 +193,7 @@ export class Thumb extends Model {
for (let j = 0; j < p.Files.length; j++) {
let f = p.Files[j];
if (!f || f.Type !== "jpg") {
if (!f || f.FileType !== "jpg") {
continue;
}

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -2,7 +2,7 @@ import { timeZonesNames } from "@vvo/tzdb";
import { $gettext } from "common/vm";
import { Info } from "luxon";
import { config } from "app/session";
import { TypeImage, TypeLive, TypeRaw, TypeVideo } from "model/photo";
import { MediaAnimated, MediaImage, MediaLive, MediaRaw, MediaVideo } from "model/photo";
export const TimeZones = () =>
[
@ -304,19 +304,23 @@ export const MapsStyle = () => [
export const PhotoTypes = () => [
{
text: $gettext("Image"),
value: TypeImage,
value: MediaImage,
},
{
text: $gettext("Animated"),
value: MediaAnimated,
},
{
text: $gettext("Raw"),
value: TypeRaw,
value: MediaRaw,
},
{
text: $gettext("Live"),
value: TypeLive,
value: MediaLive,
},
{
text: $gettext("Video"),
value: TypeVideo,
value: MediaVideo,
},
];

View file

@ -666,10 +666,10 @@
<h4><a name="trademark"></a>18. PhotoPrism® Trademark and Brand Assets</h4>
<p>
(a) PhotoPrisms Brand Assets including trademarks, logos, icons, fonts,
corporate design, product and service names, and any other brand features and
elements, whether registered or unregistered (Brand Assets) are proprietary
assets owned exclusively by Michael Mayer and his legal successors (PhotoPrism).
We reserve the right to object to any use or misuse in any jurisdiction worldwide.
corporate design, product and service names, and any other brand features
and elements, whether registered or unregistered (Brand Assets) are
proprietary assets owned exclusively by PhotoPrism UG (PhotoPrism). We
reserve the right to object to any use or misuse in any jurisdiction worldwide.
Visit <a target="_blank" href="https://photoprism.app/trademark">photoprism.app/trademark</a>
to learn more.
</p>

View file

@ -46,7 +46,7 @@
</template>
<script>
import {Photo, TypeLive, TypeRaw, TypeVideo} from "model/photo";
import {Photo, MediaLive, MediaRaw, MediaVideo, MediaAnimated} from "model/photo";
import Album from "model/album";
import Thumb from "model/thumb";
import Event from "pubsub-js";
@ -79,7 +79,7 @@ export default {
uid: uid,
results: [],
scrollDisabled: true,
scrollDistance: window.innerHeight*2,
scrollDistance: window.innerHeight * 2,
batchSize: batchSize,
offset: 0,
page: 0,
@ -99,7 +99,7 @@ export default {
};
},
computed: {
selectMode: function() {
selectMode: function () {
return this.selection.length > 0;
},
},
@ -182,11 +182,11 @@ export default {
const selected = this.results[index];
// Don't open as stack when user is selecting pictures, or a RAW has only one JPEG.
if (this.selection.length > 0 || selected.Type === TypeRaw && selected.jpegFiles().length < 2) {
if (this.selection.length > 0 || selected.Type === MediaRaw && selected.jpegFiles().length < 2) {
showMerged = false;
}
if (showMerged && selected.Type === TypeLive || selected.Type === TypeVideo) {
if (showMerged && selected.Type === MediaLive || selected.Type === MediaVideo || selected.Type === MediaAnimated) {
if (selected.isPlayable()) {
this.$viewer.play({video: selected, album: this.album});
} else {

View file

@ -42,7 +42,7 @@
</template>
<script>
import {Photo, TypeLive, TypeRaw, TypeVideo} from "model/photo";
import {Photo, MediaLive, MediaRaw, MediaVideo, MediaAnimated} from "model/photo";
import Thumb from "model/thumb";
import Viewer from "common/viewer";
import Event from "pubsub-js";
@ -228,11 +228,11 @@ export default {
const selected = this.results[index];
// Don't open as stack when user is selecting pictures, or a RAW has only one JPEG.
if (this.selection.length > 0 || selected.Type === TypeRaw && selected.jpegFiles().length < 2) {
if (this.selection.length > 0 || selected.Type === MediaRaw && selected.jpegFiles().length < 2) {
showMerged = false;
}
if (showMerged && selected.Type === TypeLive || selected.Type === TypeVideo) {
if (showMerged && selected.Type === MediaLive || selected.Type === MediaVideo|| selected.Type === MediaAnimated) {
if (selected.isPlayable()) {
this.$viewer.play({video: selected});
} else {

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -42,7 +42,7 @@
@mouseover="playLive(photo)"
@mouseleave="pauseLive(photo)"
>
<v-layout v-if="photo.Type === 'live'" class="live-player">
<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>
<source :src="photo.videoUrl()">
@ -109,7 +109,7 @@
{{ photo.getDateString(true) }}
</div>
<template v-if="!photo.Description">
<div v-if="photo.Type === 'video'" :title="labels.video">
<div v-if="photo.Type === 'video' || photo.Type === 'animated'" :title="labels.video">
<v-icon size="14">movie</v-icon>
{{ photo.getVideoInfo() }}
</div>

View file

@ -48,7 +48,7 @@
<v-icon color="white" class="select-on">check_circle</v-icon>
<v-icon color="white" class="select-off">radio_button_off</v-icon>
</v-btn>
<v-btn v-else-if="props.item.Type === 'video' || props.item.Type === 'live'"
<v-btn v-else-if="props.item.Type === 'video' || props.item.Type === 'live' || props.item.Type === 'animated'"
:ripple="false"
flat icon large absolute class="input-open"
@click.stop.prevent="openPhoto(props.index, true)">
@ -186,7 +186,7 @@ export default {
} else if (this.photos[index]) {
let photo = this.photos[index];
if (photo.Type === 'video' && photo.isPlayable()) {
if ((photo.Type === 'video' || photo.Type === 'animated') && photo.isPlayable()) {
this.openPhoto(index, true);
} else {
this.openPhoto(index, false);

View file

@ -41,7 +41,7 @@
@mouseover="playLive(photo)"
@mouseleave="pauseLive(photo)"
>
<v-layout v-if="photo.Type === 'live'" class="live-player">
<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>
<source :src="photo.videoUrl()">

View file

@ -87,7 +87,7 @@
</template>
<script>
import {Photo, TypeLive, TypeRaw, TypeVideo} from "model/photo";
import {Photo, MediaLive, MediaRaw, MediaVideo, MediaAnimated} from "model/photo";
import Album from "model/album";
import Thumb from "model/thumb";
import Event from "pubsub-js";
@ -238,11 +238,11 @@ export default {
const selected = this.results[index];
// Don't open as stack when user is selecting pictures, or a RAW has only one JPEG.
if (this.selection.length > 0 || selected.Type === TypeRaw && selected.jpegFiles().length < 2) {
if (this.selection.length > 0 || selected.Type === MediaRaw && selected.jpegFiles().length < 2) {
showMerged = false;
}
if (showMerged && selected.Type === TypeLive || selected.Type === TypeVideo) {
if (showMerged && selected.Type === MediaLive || selected.Type === MediaVideo|| selected.Type === MediaAnimated) {
if (selected.isPlayable()) {
this.$viewer.play({video: selected, album: this.album});
} else {

View file

@ -2,7 +2,7 @@ const clientConfig = {
mode: "user",
name: "PhotoPrism",
version: "210710-bae1f2d7-Linux-x86_64-DEBUG",
copyright: "(c) 2018-2022 Michael Mayer <hello@photoprism.app>",
copyright: "(c) 2018-2022 PhotoPrism UG. All rights reserved.",
flags: "public debug experimental settings",
baseUri: "",
staticUri: "/static",

View file

@ -56,7 +56,7 @@ Mock.onPost("api/v1/photos/pqbemz8276mhtobh/files/fqbfk181n4ca5sud/primary").rep
UID: "fqbfk181n4ca5sud",
Name: "1980/01/superCuteKitten.mp4",
Primary: true,
Type: "mp4",
FileType: "mp4",
Hash: "1xxbgdt55",
},
],
@ -75,7 +75,7 @@ Mock.onPut("api/v1/photos/pqbemz8276mhtobh").reply(
UID: "fqbfk181n4ca5sud",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Hash: "1xxbgdt55",
},
],

View file

@ -74,7 +74,7 @@ describe("model/file", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Name: "1/2/IMG123.jpg",
};
const file = new File(values);
@ -91,7 +91,7 @@ describe("model/file", () => {
InstanceID: 5,
UID: "ABC123",
Name: "1/2/IMG123.jpg",
Type: "raw",
FileType: "raw",
};
const file3 = new File(values3);
assert.equal(file3.thumbnailUrl("tile_224"), "/api/v1/svg/raw");
@ -99,7 +99,7 @@ describe("model/file", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Name: "1/2/IMG123.jpg",
Sidecar: true,
};
@ -112,7 +112,7 @@ describe("model/file", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Name: "1/2/IMG123.jpg",
};
const file = new File(values);
@ -123,7 +123,7 @@ describe("model/file", () => {
const values = {
InstanceID: 5,
UID: "ABC123",
Type: "jpg",
FileType: "jpg",
Name: "1/2/IMG123.jpg",
};
const file = new File(values);
@ -135,7 +135,7 @@ describe("model/file", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Width: 500,
Height: 700,
Name: "1/2/IMG123.jpg",
@ -147,7 +147,7 @@ describe("model/file", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Width: 900,
Height: 850,
Name: "1/2/IMG123.jpg",
@ -159,7 +159,7 @@ describe("model/file", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Width: 750,
Height: 850,
Name: "1/2/IMG123.jpg",
@ -174,7 +174,7 @@ describe("model/file", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Name: "1/2/IMG123.jpg",
CreatedAt: "2012-07-08T14:45:39Z",
UpdatedAt: "2012-07-08T14:45:39Z",
@ -188,7 +188,7 @@ describe("model/file", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Duration: 8009,
Name: "1/2/IMG123.jpg",
CreatedAt: "2012-07-08T14:45:39Z",
@ -203,7 +203,7 @@ describe("model/file", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Duration: 8009,
Name: "1/2/IMG123.jpg",
CreatedAt: "2012-07-08T14:45:39Z",
@ -215,7 +215,7 @@ describe("model/file", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Duration: 8009,
Name: "1/2/IMG123.jpg",
Video: true,
@ -228,7 +228,7 @@ describe("model/file", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Duration: 8009,
Name: "1/2/IMG123.jpg",
Sidecar: true,
@ -244,7 +244,7 @@ describe("model/file", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Size: 8009,
Name: "1/2/IMG123.jpg",
CreatedAt: "2012-07-08T14:45:39Z",
@ -256,7 +256,7 @@ describe("model/file", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Size: 8009999987,
Name: "1/2/IMG123.jpg",
CreatedAt: "2012-07-08T14:45:39Z",
@ -268,7 +268,7 @@ describe("model/file", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Size: 8009999987,
Name: "1/2/IMG123.jpg",
Width: 500,
@ -285,7 +285,7 @@ describe("model/file", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Duration: 8009,
Favorite: false,
Name: "1/2/IMG123.jpg",
@ -303,7 +303,7 @@ describe("model/file", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Duration: 8009,
Favorite: true,
Name: "1/2/IMG123.jpg",
@ -321,7 +321,7 @@ describe("model/file", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Duration: 8009,
Favorite: true,
Name: "1/2/IMG123.jpg",
@ -342,7 +342,7 @@ describe("model/file", () => {
PhotoUID: "bgad457",
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Duration: 8009,
Favorite: true,
Name: "1/2/IMG123.jpg",

View file

@ -46,7 +46,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Width: 500,
Height: 600,
Hash: "1xxbgdt55",
@ -64,7 +64,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Width: 500,
Height: 600,
},
@ -88,7 +88,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Video: true,
Width: 500,
Height: 600,
@ -98,7 +98,7 @@ describe("model/photo", () => {
UID: "123fde",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Width: 500,
Height: 600,
Hash: "1xxbgdkkk",
@ -415,7 +415,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.jpg",
Primary: true,
Type: "jpg",
FileType: "jpg",
Hash: "1xxbgdt55",
},
],
@ -501,7 +501,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.jpg",
Primary: true,
Type: "TypeJpeg",
FileType: "TypeJpeg",
Width: 500,
Height: 600,
Hash: "1xxbgdt53",
@ -532,7 +532,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.jpg",
Primary: true,
Type: "TypeJpeg",
FileType: "TypeJpeg",
Width: 500,
Height: 600,
Hash: "1xxbgdt53",
@ -560,7 +560,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.jpg",
Primary: true,
Type: "TypeJpeg",
FileType: "TypeJpeg",
Width: 500,
Height: 600,
Hash: "1xxbgdt53",
@ -583,7 +583,7 @@ describe("model/photo", () => {
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Video: true,
Type: "mp4",
FileType: "mp4",
Width: 500,
Height: 600,
Hash: "1xxbgdt55",
@ -603,7 +603,7 @@ describe("model/photo", () => {
Name: "1980/01/superCuteKitten.jpg",
Primary: false,
Video: true,
Type: "jpg",
FileType: "jpg",
Width: 500,
Height: 600,
Hash: "1xxbgdt53",
@ -627,7 +627,7 @@ describe("model/photo", () => {
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Video: true,
Type: "mp4",
FileType: "mp4",
Width: 900,
Height: 600,
Hash: "1xxbgdt55",
@ -651,7 +651,7 @@ describe("model/photo", () => {
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Video: true,
Type: "mp4",
FileType: "mp4",
Width: 0,
Height: 0,
Hash: "1xxbgdt55",
@ -685,7 +685,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.jpg",
Primary: true,
Type: "jpg",
FileType: "jpg",
Width: 500,
Height: 600,
Hash: "1xxbgdt53",
@ -707,7 +707,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Width: 500,
Height: 600,
Hash: "1xxbgdt55",
@ -727,7 +727,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.jpg",
Primary: false,
Type: "jpg",
FileType: "jpg",
Width: 500,
Height: 600,
Hash: "1xxbgdt53",
@ -752,7 +752,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.jpg",
Primary: true,
Type: "jpg",
FileType: "jpg",
Width: 500,
Height: 600,
Hash: "1xxbgdt53",
@ -777,7 +777,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Width: 500,
Height: 600,
Hash: "1xxbgdt55",
@ -785,6 +785,7 @@ describe("model/photo", () => {
],
};
const photo3 = new Photo(values3);
assert.equal(photo3.videoUrl(), "/api/v1/videos/1xxbgdt55/public/avc");
const values4 = {
ID: 1,
@ -796,7 +797,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.jpg",
Primary: false,
Type: "jpg",
FileType: "jpg",
Width: 500,
Height: 600,
Hash: "1xxbgdt53",
@ -804,6 +805,7 @@ describe("model/photo", () => {
},
],
};
const photo4 = new Photo(values4);
assert.equal(photo4.videoUrl(), "/api/v1/videos/1xxbgdt53/public/avc");
});
@ -820,7 +822,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Width: 500,
Height: 600,
Hash: "1xxbgdt55",
@ -829,7 +831,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.jpg",
Primary: false,
Type: "jpg",
FileType: "jpg",
Width: 500,
Height: 600,
Hash: "1xxbgdt56",
@ -847,7 +849,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/NotMainKitten.jpg",
Primary: false,
Type: "jpg",
FileType: "jpg",
Width: 500,
Height: 600,
Hash: "1xxbgdt53",
@ -856,7 +858,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/MainKitten.jpg",
Primary: true,
Type: "jpg",
FileType: "jpg",
Width: 500,
Height: 600,
Hash: "1xxbgdt54",
@ -883,7 +885,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.jpg",
Primary: false,
Type: FormatJpeg,
FileType: FormatJpeg,
Width: 500,
Height: 600,
Hash: "1xxbgdt55",
@ -892,7 +894,7 @@ describe("model/photo", () => {
UID: "123fgz",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Width: 500,
Height: 600,
Hash: "1xxbgdt66",
@ -916,7 +918,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Width: 500,
Height: 600,
Hash: "1xxbgdt55",
@ -925,7 +927,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.jpg",
Primary: false,
Type: "jpg",
FileType: "jpg",
Width: 500,
Height: 600,
Hash: "1xxbgdt56",
@ -948,7 +950,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/cat.jpg",
Primary: false,
Type: "jpg",
FileType: "jpg",
Width: 500,
Height: 600,
Hash: "1xxbgdt55",
@ -957,7 +959,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1999/01/dog.jpg",
Primary: true,
Type: "jpg",
FileType: "jpg",
Width: 500,
Height: 600,
Hash: "1xxbgdt56",
@ -974,7 +976,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/cat.jpg",
Primary: true,
Type: "jpg",
FileType: "jpg",
Width: 500,
Height: 600,
Hash: "1xxbgdt55",
@ -983,7 +985,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1999/01/dog.jpg",
Primary: false,
Type: "jpg",
FileType: "jpg",
Width: 500,
Height: 600,
Hash: "1xxbgdt56",
@ -1000,7 +1002,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/cat.jpg",
Primary: true,
Type: "jpg",
FileType: "jpg",
Width: 500,
Height: 600,
Hash: "1xxbgdt55",
@ -1044,7 +1046,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Hash: "1xxbgdt55",
},
],
@ -1059,7 +1061,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Width: 500,
Height: 600,
Hash: "1xxbgdt55",
@ -1079,7 +1081,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Hash: "1xxbgdt55",
Duration: 6000,
Size: 10240,
@ -1089,7 +1091,7 @@ describe("model/photo", () => {
UID: "345fgb",
Name: "1980/01/superCuteKitten.jpg",
Primary: true,
Type: "jpg",
FileType: "jpg",
Hash: "1xxbgjhu5",
Width: 300,
Height: 500,
@ -1115,7 +1117,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.jpg",
Primary: true,
Type: "jpg",
FileType: "jpg",
Hash: "1xxbgdt55",
},
],
@ -1137,7 +1139,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Width: 500,
Height: 600,
Hash: "1xxbgdt55",
@ -1156,7 +1158,7 @@ describe("model/photo", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Width: 500,
Height: 600,
Hash: "1xxbgdt55",
@ -1168,7 +1170,7 @@ describe("model/photo", () => {
UID: "123fgx",
Name: "1980/01/superCuteKitten.jpg",
Primary: true,
Type: "jpg",
FileType: "jpg",
Width: 800,
Height: 600,
Hash: "1xxbgdt55",
@ -1236,7 +1238,7 @@ describe("model/photo", () => {
UID: "fqbfk181n4ca5sud",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Hash: "1xxbgdt55",
},
],
@ -1262,7 +1264,7 @@ describe("model/photo", () => {
UID: "fqbfk181n4ca5sud",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Hash: "1xxbgdt55",
},
],
@ -1288,14 +1290,14 @@ describe("model/photo", () => {
UID: "fqbfk181n4ca5sud",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Hash: "1xxbgdt55",
},
{
UID: "fqbfk181n4ca5abc",
Name: "1980/01/superCuteKitten.mp4",
Primary: true,
Type: "mp4",
FileType: "mp4",
Hash: "1xxbgdt89",
},
],
@ -1396,7 +1398,7 @@ describe("model/photo", () => {
UID: "fqbfk181n4ca5sud",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Hash: "1xxbgdt55",
},
],
@ -1461,7 +1463,7 @@ describe("model/photo", () => {
UID: "fqbfk181n4ca5sud",
Name: "1980/01/superCuteKitten.mp4",
Primary: false,
Type: "mp4",
FileType: "mp4",
Hash: "1xxbgdt55",
},
],
@ -1482,7 +1484,7 @@ describe("model/photo", () => {
UID: "fqbfk181n4ca5sud",
Name: "1980/01/superCuteKitten.mp4",
Primary: true,
Type: "mp4",
FileType: "mp4",
Hash: "1xxbgdt55",
Markers: [
{

View file

@ -133,7 +133,7 @@ describe("model/thumb", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.jpg",
Primary: true,
Type: "jpg",
FileType: "jpg",
Width: 500,
Height: 600,
Hash: "1xxbgdt53",
@ -157,7 +157,7 @@ describe("model/thumb", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.jpg",
Primary: true,
Type: "mov",
FileType: "mov",
Width: 500,
Height: 600,
Hash: "1xxbgdt53",
@ -190,7 +190,7 @@ describe("model/thumb", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.jpg",
Primary: true,
Type: "jpg",
FileType: "jpg",
Width: 500,
Height: 600,
Hash: "1xxbgdt53",
@ -238,7 +238,7 @@ describe("model/thumb", () => {
UID: "123fgb",
Name: "1980/01/superCuteKitten.jpg",
Primary: true,
Type: "jpg",
FileType: "jpg",
Width: 500,
Height: 600,
Hash: "1xxbgdt53",
@ -258,7 +258,7 @@ describe("model/thumb", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Name: "1/2/IMG123.jpg",
};
const file = new File(values);
@ -266,7 +266,7 @@ describe("model/thumb", () => {
const values2 = {
InstanceID: 5,
UID: "ABC123",
Type: "jpg",
FileType: "jpg",
Name: "1/2/IMG123.jpg",
};
const file2 = new File(values2);
@ -278,7 +278,7 @@ describe("model/thumb", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Name: "1/2/IMG123.jpg",
};
const file = new File(values);
@ -297,7 +297,7 @@ describe("model/thumb", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Width: 900,
Height: 850,
Name: "1/2/IMG123.jpg",
@ -310,7 +310,7 @@ describe("model/thumb", () => {
InstanceID: 5,
UID: "ABC123",
Hash: "54ghtfd",
Type: "jpg",
FileType: "jpg",
Width: 750,
Height: 850,
Name: "1/2/IMG123.jpg",

View file

@ -1,6 +1,6 @@
/*
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -15,7 +15,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -2,7 +2,7 @@
Package acl contains PhotoPrism's access control lists for authorizing user actions.
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -17,7 +17,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -2,7 +2,7 @@
Package api contains PhotoPrism REST API handlers.
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -17,7 +17,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -5,6 +5,8 @@ import (
"os"
"path/filepath"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/gin-gonic/gin"
"gopkg.in/yaml.v2"
@ -69,10 +71,10 @@ func SaveConfigOptions(router *gin.RouterGroup) {
return
}
fileName := conf.ConfigFile()
fileName := conf.OptionsYaml()
if fileName == "" {
log.Errorf("options: empty config file name")
log.Errorf("config: empty options.yml file path")
AbortSaveFailed(c)
return
}
@ -85,20 +87,20 @@ func SaveConfigOptions(router *gin.RouterGroup) {
yamlData, err := os.ReadFile(fileName)
if err != nil {
log.Errorf("options: %s", err)
log.Errorf("config: failed loading values from %s (%s)", sanitize.Log(fileName), err)
c.AbortWithStatusJSON(http.StatusInternalServerError, err)
return
}
if err := yaml.Unmarshal(yamlData, v); err != nil {
log.Errorf("options: %s", err)
log.Warnf("config: failed parsing values in %s (%s)", sanitize.Log(fileName), err)
c.AbortWithStatusJSON(http.StatusInternalServerError, err)
return
}
}
if err := c.BindJSON(&v); err != nil {
log.Errorf("options: %s", err)
log.Errorf("config: %s (bind json)", err)
AbortBadRequest(c)
return
}
@ -106,27 +108,28 @@ func SaveConfigOptions(router *gin.RouterGroup) {
yamlData, err := yaml.Marshal(v)
if err != nil {
log.Errorf("options: %s", err)
log.Errorf("config: %s (marshal yaml)", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, err)
return
}
// Make sure directory exists.
if err := os.MkdirAll(filepath.Dir(fileName), os.ModePerm); err != nil {
log.Errorf("options: %s", err)
log.Errorf("config: failed creating config path %s (%s)", filepath.Dir(fileName), err)
c.AbortWithStatusJSON(http.StatusInternalServerError, err)
return
}
// Write YAML data to file.
if err := os.WriteFile(fileName, yamlData, os.ModePerm); err != nil {
log.Errorf("options: %s", err)
log.Errorf("config: failed writing values to %s (%s)", sanitize.Log(fileName), err)
c.AbortWithStatusJSON(http.StatusInternalServerError, err)
return
}
// Reload options.
if err := conf.Options().Load(fileName); err != nil {
log.Errorf("options: %s", err)
log.Warnf("config: failed loading values from %s (%s)", sanitize.Log(fileName), err)
c.AbortWithStatusJSON(http.StatusInternalServerError, err)
return
}

View file

@ -179,7 +179,7 @@ func PhotoUnstack(router *gin.RouterGroup) {
}
// Reset type for existing photo stack to image.
if err := stackPhoto.Update("PhotoType", entity.TypeImage); err != nil {
if err := stackPhoto.Update("PhotoType", entity.MediaImage); err != nil {
log.Errorf("photo: %s (unstack %s)", err, sanitize.Log(baseName))
AbortUnexpected(c)
return

View file

@ -44,7 +44,7 @@ func TestGetFoldersOriginals(t *testing.T) {
for _, folder := range folders {
assert.Equal(t, "", folder.FolderDescription)
assert.Equal(t, entity.TypeDefault, folder.FolderType)
assert.Equal(t, entity.MediaUnknown, folder.FolderType)
assert.Equal(t, entity.SortOrderName, folder.FolderOrder)
assert.Equal(t, entity.RootOriginals, folder.Root)
assert.IsType(t, "", folder.FolderUID)
@ -81,7 +81,7 @@ func TestGetFoldersOriginals(t *testing.T) {
for _, folder := range folders {
assert.Equal(t, "", folder.FolderDescription)
assert.Equal(t, entity.TypeDefault, folder.FolderType)
assert.Equal(t, entity.MediaUnknown, folder.FolderType)
assert.Equal(t, entity.SortOrderName, folder.FolderOrder)
assert.Equal(t, entity.RootOriginals, folder.Root)
assert.IsType(t, "", folder.FolderUID)
@ -127,7 +127,7 @@ func TestGetFoldersImport(t *testing.T) {
for _, folder := range folders {
assert.Equal(t, "", folder.FolderDescription)
assert.Equal(t, entity.TypeDefault, folder.FolderType)
assert.Equal(t, entity.MediaUnknown, folder.FolderType)
assert.Equal(t, entity.SortOrderName, folder.FolderOrder)
assert.Equal(t, entity.RootImport, folder.Root)
assert.IsType(t, "", folder.FolderUID)
@ -164,7 +164,7 @@ func TestGetFoldersImport(t *testing.T) {
for _, folder := range folders {
assert.Equal(t, "", folder.FolderDescription)
assert.Equal(t, entity.TypeDefault, folder.FolderType)
assert.Equal(t, entity.MediaUnknown, folder.FolderType)
assert.Equal(t, entity.SortOrderName, folder.FolderOrder)
assert.Equal(t, entity.RootImport, folder.Root)
assert.IsType(t, "", folder.FolderUID)

View file

@ -52,7 +52,7 @@ func SaveSettings(router *gin.RouterGroup) {
return
}
if err := settings.Save(conf.SettingsFile()); err != nil {
if err := settings.Save(conf.SettingsYaml()); err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, err)
return
}

View file

@ -2,7 +2,7 @@
Package auto contains index & import background workers.
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -17,7 +17,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -2,7 +2,7 @@
Package classify encapsulates image classification using TensorFlow.
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -17,7 +17,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -2,7 +2,7 @@
Package commands provides photoprism CLI (sub-)commands.
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -17,7 +17,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -23,12 +23,12 @@ func TestShowConfigCommand(t *testing.T) {
}
// Expected config command output.
assert.Contains(t, output, "config-file")
assert.Contains(t, output, "darktable-cli")
assert.Contains(t, output, "config-path")
assert.Contains(t, output, "originals-path")
assert.Contains(t, output, "import-path")
assert.Contains(t, output, "cache-path")
assert.Contains(t, output, "assets-path")
assert.Contains(t, output, "darktable-cli")
}
func TestShowFiltersCommand(t *testing.T) {

View file

@ -411,8 +411,8 @@ func (c *Config) UserConfig() ClientConfig {
Table("photos").
Select("SUM(photo_type = 'video' AND photo_quality >= 0 AND photo_private = 0) AS videos, " +
"SUM(photo_type = 'live' AND photo_quality >= 0 AND photo_private = 0) AS live, " +
"SUM(photo_quality = -1) AS hidden, SUM(photo_type IN ('image','raw') AND photo_private = 0 AND photo_quality >= 0) AS photos, " +
"SUM(photo_type IN ('image','raw','live') AND photo_quality < 3 AND photo_quality >= 0 AND photo_private = 0) AS review, " +
"SUM(photo_quality = -1) AS hidden, SUM(photo_type IN ('image','raw','animated') AND photo_private = 0 AND photo_quality >= 0) AS photos, " +
"SUM(photo_type IN ('image','raw','live','animated') AND photo_quality < 3 AND photo_quality >= 0 AND photo_private = 0) AS review, " +
"SUM(photo_favorite = 1 AND photo_private = 0 AND photo_quality >= 0) AS favorites, " +
"SUM(photo_private = 1 AND photo_quality >= 0) AS private").
Where("photos.id NOT IN (SELECT photo_id FROM files WHERE file_primary = 1 AND (file_missing = 1 OR file_error <> ''))").

View file

@ -2,7 +2,7 @@
Package config contains CLI config related config functionality.
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -17,7 +17,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -2,7 +2,7 @@
Package crop provides image crop data structures and helpers.
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -17,7 +17,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -1,5 +1,7 @@
package entity
import "github.com/photoprism/photoprism/pkg/fs"
// Panorama Projection Types
// TODO: Move to separate package.
@ -12,19 +14,19 @@ const (
ProjPseudocylindricalCompromise = "pseudocylindrical-compromise"
)
// Content Types
// Media Types.
const (
TypeDefault = ""
TypeImage = "image"
TypeLive = "live"
TypeVideo = "video"
TypeRaw = "raw"
TypeText = "text"
MediaUnknown = ""
MediaImage = string(fs.MediaImage)
MediaVector = string(fs.MediaVector)
MediaAnimated = "animated"
MediaLive = "live"
MediaVideo = string(fs.MediaVideo)
MediaRaw = string(fs.MediaRaw)
TypeMeta = "meta"
)
// Root Directories Types
// Root Dirs.
const (
RootUnknown = ""
RootOriginals = "/"
@ -34,8 +36,7 @@ const (
RootPath = "/"
)
// Unknown Values
// Defaults.
const (
UnknownYear = -1
UnknownMonth = -1

View file

@ -25,6 +25,8 @@ type Details struct {
CopyrightSrc string `gorm:"type:VARBINARY(8);" json:"CopyrightSrc" yaml:"CopyrightSrc,omitempty"`
License string `gorm:"type:VARCHAR(1024);" json:"License" yaml:"License,omitempty"`
LicenseSrc string `gorm:"type:VARBINARY(8);" json:"LicenseSrc" yaml:"LicenseSrc,omitempty"`
Software string `gorm:"type:VARCHAR(1024);" json:"Software" yaml:"Software,omitempty"`
SoftwareSrc string `gorm:"type:VARBINARY(8);" json:"SoftwareSrc" yaml:"SoftwareSrc,omitempty"`
CreatedAt time.Time `yaml:"-"`
UpdatedAt time.Time `yaml:"-"`
}
@ -109,6 +111,11 @@ func (m *Details) NoLicense() bool {
return m.License == ""
}
// NoSoftware tests if the photo has no Software.
func (m *Details) NoSoftware() bool {
return m.Software == ""
}
// HasKeywords tests if the photo has a Keywords.
func (m *Details) HasKeywords() bool {
return !m.NoKeywords()
@ -139,6 +146,11 @@ func (m *Details) HasLicense() bool {
return !m.NoLicense()
}
// HasSoftware tests if the photo has a Software.
func (m *Details) HasSoftware() bool {
return !m.NoSoftware()
}
// SetKeywords updates the photo details field.
func (m *Details) SetKeywords(data, src string) {
val := txt.Clip(data, txt.ClipText)
@ -242,3 +254,19 @@ func (m *Details) SetLicense(data, src string) {
m.License = val
m.LicenseSrc = src
}
// SetSoftware updates the photo details field.
func (m *Details) SetSoftware(data, src string) {
val := txt.Clip(data, txt.ClipShortText)
if val == "" {
return
}
if (SrcPriority[src] < SrcPriority[m.SoftwareSrc]) && m.HasSoftware() {
return
}
m.Software = val
m.SoftwareSrc = src
}

View file

@ -332,3 +332,27 @@ func TestDetails_SetLicense(t *testing.T) {
assert.Equal(t, "new", description.License)
})
}
func TestDetails_SetSoftware(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
description := &Details{PhotoID: 123, Software: ""}
assert.False(t, description.HasSoftware())
description.SetSoftware("", "manual")
assert.False(t, description.HasSoftware())
})
t.Run("NoPriority", func(t *testing.T) {
description := &Details{PhotoID: 123, Software: "old", SoftwareSrc: SrcManual}
assert.Equal(t, "old", description.Software)
description.SetSoftware("new", SrcAuto)
assert.Equal(t, "old", description.Software)
})
t.Run("NewValue", func(t *testing.T) {
description := &Details{PhotoID: 123, Software: "old", SoftwareSrc: SrcMeta}
assert.Equal(t, "old", description.Software)
description.SetSoftware("new", SrcManual)
assert.Equal(t, "new", description.Software)
})
}

View file

@ -2,6 +2,7 @@ package entity
import (
"fmt"
"math"
"path/filepath"
"sort"
"strings"
@ -14,7 +15,6 @@ import (
"github.com/ulule/deepcopier"
"github.com/photoprism/photoprism/internal/face"
"github.com/photoprism/photoprism/pkg/colors"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/rnd"
@ -44,6 +44,7 @@ type File struct {
PhotoID uint `gorm:"index:idx_files_photo_id;" json:"-" yaml:"-"`
PhotoUID string `gorm:"type:VARBINARY(42);index;" json:"PhotoUID" yaml:"PhotoUID"`
PhotoTakenAt time.Time `gorm:"type:DATETIME;index;" json:"TakenAt" yaml:"TakenAt"`
MetaUTC int64 `gorm:"column:meta_utc;index;" json:"MetaUTC" yaml:"MetaUTC,omitempty"`
TimeIndex *string `gorm:"type:VARBINARY(48);" json:"TimeIndex" yaml:"TimeIndex"`
MediaID *string `gorm:"type:VARBINARY(32);" json:"MediaID" yaml:"MediaID"`
InstanceID string `gorm:"type:VARBINARY(42);index;" json:"InstanceID,omitempty" yaml:"InstanceID,omitempty"`
@ -54,7 +55,8 @@ type File struct {
FileHash string `gorm:"type:VARBINARY(128);index" json:"Hash" yaml:"Hash,omitempty"`
FileSize int64 `json:"Size" yaml:"Size,omitempty"`
FileCodec string `gorm:"type:VARBINARY(32)" json:"Codec" yaml:"Codec,omitempty"`
FileType string `gorm:"type:VARBINARY(32)" json:"Type" yaml:"Type,omitempty"`
FileType string `gorm:"type:VARBINARY(16)" json:"FileType" yaml:"FileType,omitempty"`
MediaType string `gorm:"type:VARBINARY(16)" json:"MediaType" yaml:"MediaType,omitempty"`
FileMime string `gorm:"type:VARBINARY(64)" json:"Mime" yaml:"Mime,omitempty"`
FilePrimary bool `gorm:"index:idx_files_photo_id;" json:"Primary" yaml:"Primary,omitempty"`
FileSidecar bool `json:"Sidecar" yaml:"Sidecar,omitempty"`
@ -62,18 +64,22 @@ type File struct {
FilePortrait bool `json:"Portrait" yaml:"Portrait,omitempty"`
FileVideo bool `json:"Video" yaml:"Video,omitempty"`
FileDuration time.Duration `json:"Duration" yaml:"Duration,omitempty"`
FileFPS float64 `gorm:"column:file_fps;" json:"FPS" yaml:"FPS,omitempty"`
FileFrames int `json:"Frames" yaml:"Frames,omitempty"`
FileWidth int `json:"Width" yaml:"Width,omitempty"`
FileHeight int `json:"Height" yaml:"Height,omitempty"`
FileOrientation int `json:"Orientation" yaml:"Orientation,omitempty"`
FileProjection string `gorm:"type:VARBINARY(64);" json:"Projection,omitempty" yaml:"Projection,omitempty"`
FileAspectRatio float32 `gorm:"type:FLOAT;" json:"AspectRatio" yaml:"AspectRatio,omitempty"`
FileHDR bool `gorm:"column:file_hdr;" json:"IsHDR" yaml:"IsHDR,omitempty"`
FileHDR bool `gorm:"column:file_hdr;" json:"HDR" yaml:"HDR,omitempty"`
FileWatermark bool `gorm:"column:file_watermark;" json:"Watermark" yaml:"Watermark,omitempty"`
FileColorProfile string `gorm:"type:VARBINARY(64);" json:"ColorProfile,omitempty" yaml:"ColorProfile,omitempty"`
FileMainColor string `gorm:"type:VARBINARY(16);index;" json:"MainColor" yaml:"MainColor,omitempty"`
FileColors string `gorm:"type:VARBINARY(9);" json:"Colors" yaml:"Colors,omitempty"`
FileLuminance string `gorm:"type:VARBINARY(9);" json:"Luminance" yaml:"Luminance,omitempty"`
FileDiff uint32 `json:"Diff" yaml:"Diff,omitempty"`
FileChroma uint8 `json:"Chroma" yaml:"Chroma,omitempty"`
FileSoftware string `gorm:"type:VARCHAR(64)" json:"Software" yaml:"Software,omitempty"`
FileError string `gorm:"type:VARBINARY(512)" json:"Error" yaml:"Error,omitempty"`
ModTime int64 `json:"ModTime" yaml:"-"`
CreatedAt time.Time `json:"CreatedAt" yaml:"-"`
@ -574,6 +580,16 @@ func (m *File) ResetHDR() {
m.FileHDR = false
}
// HasWatermark returns true if the file has a watermark.
func (m *File) HasWatermark() bool {
return m.FileWatermark
}
// IsAnimated returns true if the file has animated image frames.
func (m *File) IsAnimated() bool {
return m.FileFrames > 1 && m.MediaType == MediaImage
}
// ColorProfile returns the ICC color profile name if any.
func (m *File) ColorProfile() string {
return SanitizeStringType(m.FileColorProfile)
@ -596,6 +612,69 @@ func (m *File) ResetColorProfile() {
m.FileColorProfile = ""
}
// SetSoftware sets the software name.
func (m *File) SetSoftware(name string) {
if name = SanitizeStringType(name); name != "" {
m.FileSoftware = name
}
}
// SetDuration sets the video/animation duration.
func (m *File) SetDuration(d time.Duration) {
if d <= 0 {
return
}
m.FileDuration = d.Round(time.Second)
// Update number of frames.
if m.FileFrames <= 0 && m.FileFPS > 0 {
m.FileFrames = int(math.Round(m.FileFPS * m.FileDuration.Seconds()))
}
// Update number of frames per second.
if m.FileFPS <= 0 && m.FileFrames > 0 {
m.FileFPS = float64(m.FileFrames) / m.FileDuration.Seconds()
}
}
// SetFPS sets the average number of frames per second.
func (m *File) SetFPS(frameRate float64) {
if frameRate <= 0 {
return
}
m.FileFPS = frameRate
// Update number of frames.
if m.FileFrames <= 0 && m.FileDuration > 0 {
m.FileFrames = int(math.Round(m.FileFPS * m.FileDuration.Seconds()))
}
}
// SetFrames sets the number of video/animation frames.
func (m *File) SetFrames(n int) {
if n <= 0 {
return
}
m.FileFrames = n
// Update FPS.
if m.FileFPS <= 0 && m.FileDuration > 0 {
m.FileFPS = float64(m.FileFrames) / m.FileDuration.Seconds()
}
}
// SetMetaUTC sets the creation date found in the metadata as a unix ms timestamp.
func (m *File) SetMetaUTC(taken time.Time) {
if taken.IsZero() {
return
}
m.MetaUTC = taken.UTC().UnixMilli()
}
// AddFaces adds face markers to the file.
func (m *File) AddFaces(faces face.Faces) {
sort.Slice(faces, func(i, j int) bool {

View file

@ -10,21 +10,27 @@ func (m *File) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
UID string
PhotoUID string
InstanceID string `json:",omitempty"`
Name string
Root string
OriginalName string `json:",omitempty"`
Hash string
Size int64
Codec string `json:",omitempty"`
Type string
Mime string `json:",omitempty"`
Primary bool
MetaUTC int64 `json:",omitempty"`
TimeIndex *string `json:",omitempty"`
MediaID *string `json:",omitempty"`
InstanceID string `json:",omitempty"`
OriginalName string `json:",omitempty"`
Codec string `json:",omitempty"`
FileType string `json:"FileType"`
MediaType string `json:"MediaType"`
Mime string `json:",omitempty"`
Sidecar bool `json:",omitempty"`
Missing bool `json:",omitempty"`
Portrait bool `json:",omitempty"`
Video bool `json:",omitempty"`
Duration time.Duration `json:",omitempty"`
FPS float64 `json:",omitempty"`
Frames int `json:",omitempty"`
Width int `json:",omitempty"`
Height int `json:",omitempty"`
Orientation int `json:",omitempty"`
@ -37,6 +43,8 @@ func (m *File) MarshalJSON() ([]byte, error) {
Diff uint32 `json:",omitempty"`
Chroma uint8 `json:",omitempty"`
HDR bool `json:",omitempty"`
Watermark bool `json:",omitempty"`
Software string `json:",omitempty"`
Error string `json:",omitempty"`
ModTime int64 `json:",omitempty"`
CreatedAt time.Time `json:",omitempty"`
@ -48,21 +56,27 @@ func (m *File) MarshalJSON() ([]byte, error) {
}{
UID: m.FileUID,
PhotoUID: m.PhotoUID,
InstanceID: m.InstanceID,
Name: m.FileName,
Root: m.FileRoot,
OriginalName: m.OriginalName,
Hash: m.FileHash,
Size: m.FileSize,
Codec: m.FileCodec,
Type: m.FileType,
Mime: m.FileMime,
Primary: m.FilePrimary,
MetaUTC: m.MetaUTC,
TimeIndex: m.TimeIndex,
MediaID: m.MediaID,
InstanceID: m.InstanceID,
OriginalName: m.OriginalName,
Codec: m.FileCodec,
FileType: m.FileType,
MediaType: m.MediaType,
Mime: m.FileMime,
Sidecar: m.FileSidecar,
Missing: m.FileMissing,
Portrait: m.FilePortrait,
Video: m.FileVideo,
Duration: m.FileDuration,
FPS: m.FileFPS,
Frames: m.FileFrames,
Width: m.FileWidth,
Height: m.FileHeight,
Orientation: m.FileOrientation,
@ -75,6 +89,8 @@ func (m *File) MarshalJSON() ([]byte, error) {
Diff: m.FileDiff,
Chroma: m.FileChroma,
HDR: m.FileHDR,
Watermark: m.FileWatermark,
Software: m.FileSoftware,
Error: m.FileError,
ModTime: m.ModTime,
CreatedAt: m.CreatedAt,

View file

@ -669,3 +669,106 @@ func TestFile_SetColorProfile(t *testing.T) {
assert.False(t, m.HasColorProfile(colors.ProfileDisplayP3))
})
}
func TestFile_SetFPS(t *testing.T) {
t.Run("FileDuration", func(t *testing.T) {
m := File{FileDuration: time.Second * 60}
assert.Equal(t, time.Minute, m.FileDuration)
assert.Equal(t, 0.0, m.FileFPS)
assert.Equal(t, 0, m.FileFrames)
m.SetFPS(10)
assert.Equal(t, time.Minute, m.FileDuration)
assert.Equal(t, 10.0, m.FileFPS)
assert.Equal(t, 600, m.FileFrames)
m.SetFPS(20)
assert.Equal(t, time.Minute, m.FileDuration)
assert.Equal(t, 20.0, m.FileFPS)
assert.Equal(t, 600, m.FileFrames)
m.FileFrames = 0
m.SetFPS(20)
assert.Equal(t, time.Minute, m.FileDuration)
assert.Equal(t, 20.0, m.FileFPS)
assert.Equal(t, 1200, m.FileFrames)
})
}
func TestFile_SetFrames(t *testing.T) {
t.Run("FileDuration", func(t *testing.T) {
m := File{FileDuration: time.Second * 60}
assert.Equal(t, time.Minute, m.FileDuration)
assert.Equal(t, 0.0, m.FileFPS)
assert.Equal(t, 0, m.FileFrames)
m.SetFrames(120)
assert.Equal(t, time.Minute, m.FileDuration)
assert.Equal(t, 2.0, m.FileFPS)
assert.Equal(t, 120, m.FileFrames)
m.SetFrames(30)
assert.Equal(t, time.Minute, m.FileDuration)
assert.Equal(t, 2.0, m.FileFPS)
assert.Equal(t, 30, m.FileFrames)
m.FileFPS = 0
m.SetFrames(30)
assert.Equal(t, time.Minute, m.FileDuration)
assert.Equal(t, 0.5, m.FileFPS)
assert.Equal(t, 30, m.FileFrames)
})
}
func TestFile_SetDuration(t *testing.T) {
t.Run("FileFPS", func(t *testing.T) {
m := File{FileFPS: 20}
assert.Equal(t, time.Duration(0), m.FileDuration)
assert.Equal(t, 20.0, m.FileFPS)
assert.Equal(t, 0, m.FileFrames)
m.SetDuration(time.Second * 10)
assert.Equal(t, time.Second*10, m.FileDuration)
assert.Equal(t, 20.0, m.FileFPS)
assert.Equal(t, 200, m.FileFrames)
m.SetDuration(time.Minute)
assert.Equal(t, time.Minute, m.FileDuration)
assert.Equal(t, 20.0, m.FileFPS)
assert.Equal(t, 200, m.FileFrames)
})
t.Run("FileFrames", func(t *testing.T) {
m := File{FileFrames: 600}
assert.Equal(t, time.Duration(0), m.FileDuration)
assert.Equal(t, 0.0, m.FileFPS)
assert.Equal(t, 600, m.FileFrames)
m.SetDuration(time.Minute)
assert.Equal(t, time.Minute, m.FileDuration)
assert.Equal(t, 10.0, m.FileFPS)
assert.Equal(t, 600, m.FileFrames)
m.FileFPS = 0
m.FileFrames = 0
m.SetDuration(time.Hour)
m.SetFrames(216000)
assert.Equal(t, time.Hour, m.FileDuration)
assert.Equal(t, 60.0, m.FileFPS)
assert.Equal(t, 216000, m.FileFrames)
})
}

View file

@ -80,7 +80,7 @@ func NewFolder(root, pathName string, modTime time.Time) Folder {
FolderUID: rnd.PPID('d'),
Root: root,
Path: pathName,
FolderType: TypeDefault,
FolderType: MediaUnknown,
FolderOrder: SortOrderName,
FolderCountry: UnknownCountry.ID,
FolderYear: year,

View file

@ -116,7 +116,7 @@ func (Photo) TableName() string {
func NewPhoto(stackable bool) Photo {
m := Photo{
PhotoTitle: UnknownTitle,
PhotoType: TypeImage,
PhotoType: MediaImage,
PhotoCountry: UnknownCountry.ID,
CameraID: UnknownCamera.ID,
LensID: UnknownLens.ID,

View file

@ -36,13 +36,13 @@ func (m *Photo) SetTakenAt(taken, local time.Time, zone, source string) {
}
// Round times to avoid jitter.
taken = taken.Round(time.Second).UTC()
taken = taken.Truncate(time.Second).UTC()
// Default local time to taken if zero or invalid.
if local.IsZero() || local.Year() < 1000 {
local = taken
} else {
local = local.Round(time.Second)
local = local.Truncate(time.Second)
}
// Don't update older date.

View file

@ -18,7 +18,7 @@ func (m *Photo) EstimateCountry() {
if SrcPriority[m.PlaceSrc] > SrcPriority[SrcEstimate] || m.HasLocation() || m.HasPlace() {
// Keep existing data.
return
} else if m.UnknownCamera() && m.PhotoType == TypeImage {
} else if m.UnknownCamera() && m.PhotoType == MediaImage {
// Don't estimate if it seems to be a non-photographic image.
return
}
@ -72,7 +72,7 @@ func (m *Photo) EstimateLocation(force bool) {
m.EstimatedAt = TimePointer()
// Don't estimate if it seems to be a non-photographic image.
if m.UnknownCamera() && m.PhotoType == TypeImage {
if m.UnknownCamera() && m.PhotoType == MediaImage {
m.RemoveLocation(SrcEstimate, false)
m.RemoveLocationLabels()
return

View file

@ -61,7 +61,7 @@ func (m *Photo) QualityScore() (score int) {
score++
}
if score < 3 && (m.PhotoType != TypeImage || m.EditedAt != nil) {
if score < 3 && (m.PhotoType != MediaImage || m.EditedAt != nil) {
score = 3
}

View file

@ -6,7 +6,7 @@ const Day = time.Hour * 24
// TimeStamp returns the current timestamp in UTC rounded to seconds.
func TimeStamp() time.Time {
return time.Now().UTC().Round(time.Second)
return time.Now().UTC().Truncate(time.Second)
}
// TimePointer returns a pointer to the current timestamp.

View file

@ -6,15 +6,61 @@ import (
)
func TestTimeStamp(t *testing.T) {
result := TimeStamp()
t.Run("UTC", func(t *testing.T) {
if TimeStamp().Location() != time.UTC {
t.Fatal("timestamp zone must be utc")
}
})
t.Run("Past", func(t *testing.T) {
if TimeStamp().After(time.Now().Add(time.Second)) {
t.Fatal("timestamp should be in the past from now")
}
})
t.Run("JSON", func(t *testing.T) {
t1 := TimeStamp().Add(time.Nanosecond * 123456)
if result.Location() != time.UTC {
t.Fatal("timestamp zone must be utc")
}
if b, err := t1.MarshalJSON(); err != nil {
t.Fatal(err)
} else {
t.Logf("JSON: %s", b)
}
})
t.Run("UnixMicro", func(t *testing.T) {
t1 := time.Date(-3000, 1, 1, 1, 1, 1, 0, time.UTC)
t2 := TimeStamp().Add(time.Nanosecond * 123456)
t3 := time.Date(3000, 1, 1, 1, 1, 1, 0, time.UTC)
if result.After(time.Now().Add(time.Second)) {
t.Fatal("timestamp should be in the past from now")
}
ms1 := t1.UnixMilli()
ms2 := t2.UnixMilli()
ms3 := t3.UnixMilli()
m1 := t1.UnixMicro()
m2 := t2.UnixMicro()
m3 := t3.UnixMicro()
t.Logf("MS1: %20d", ms1)
t.Logf("MS2: %20d", ms2)
t.Logf("MS3: %20d", ms3)
t.Logf("U1: %20d", m1)
t.Logf("U2: %20d", m2)
t.Logf("U3: %20d", m3)
i1, i2, i3 := 1e18-m1, 1e18-m2, 1e18-m3
t.Logf("ZZ: %20d", 9223372036854775807)
t.Logf("I1: %20d", i1)
t.Logf("I2: %20d", i2)
t.Logf("I3: %20d", i3)
t.Logf("T1: %20d", 1e18-i1)
t.Logf("T2: %20d", 1e18-i2)
t.Logf("T3: %20d", 1e18-i3)
t.Logf("D1: %s", time.UnixMicro(1e18-i1).String())
t.Logf("D2: %s", time.UnixMicro(1e18-i2).String())
t.Logf("D3: %s", time.UnixMicro(1e18-i3).String())
})
}
func TestTimePointer(t *testing.T) {

View file

@ -2,7 +2,7 @@
Package event provides a publish-subscribe event hub and a global logger.
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -17,7 +17,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

View file

@ -2,7 +2,7 @@
Package face provides facial recognition.
Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
Copyright (c) 2018 - 2022 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
@ -17,7 +17,7 @@ Copyright (c) 2018 - 2022 Michael Mayer <hello@photoprism.app>
which describe how our Brand Assets may be used:
<https://photoprism.app/trademark>
Feel free to send an e-mail to hello@photoprism.app if you have questions,
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:

Some files were not shown because too many files have changed in this diff Show more